├── .eslintignore ├── .github ├── FUNDING.yml └── workflows │ └── ci.yml ├── lib ├── package.json ├── index.js ├── render │ ├── renderer.js │ ├── xml.js │ └── html.js ├── from-code-point.js ├── common.js ├── node.js └── blocks.js ├── .npmignore ├── .gitignore ├── bench ├── samples │ ├── inline-backticks.md │ ├── block-hr.md │ ├── block-code.md │ ├── block-fences.md │ ├── block-lheading.md │ ├── block-heading.md │ ├── inline-em-flat.md │ ├── inline-em-nested.md │ ├── inline-em-worst.md │ ├── inline-escape.md │ ├── inline-newlines.md │ ├── block-bq-flat.md │ ├── block-ref-nested.md │ ├── rawtabs.md │ ├── inline-entity.md │ ├── inline-links-nested.md │ ├── block-bq-nested.md │ ├── block-html.md │ ├── block-ref-flat.md │ ├── block-list-nested.md │ ├── inline-autolink.md │ ├── inline-html.md │ ├── inline-links-flat.md │ ├── block-list-flat.md │ ├── lorem1.md │ └── README.md ├── bench.js ├── format_benchmarks.awk └── index.html ├── dingus ├── .gitignore ├── bower.json ├── preview.html ├── dingus.css ├── Makefile ├── index.html └── dingus.js ├── release_checklist.md ├── .editorconfig ├── bower.json ├── .eslintrc ├── Makefile ├── rollup.config.js ├── CONTRIBUTING.md ├── package.json ├── bin └── commonmark ├── LICENSE ├── test ├── smart_punct.txt ├── test.js └── regression.txt ├── README.md └── changelog.txt /.eslintignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | dingus/ 3 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [jgm] 2 | -------------------------------------------------------------------------------- /lib/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "module" 3 | } 4 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | * 2 | !bin/* 3 | !dist/* 4 | !lib/* 5 | !lib/render/* 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *.bak 3 | *.diff 4 | *# 5 | node_modules/ 6 | package-lock.json 7 | dist/*.js 8 | -------------------------------------------------------------------------------- /bench/samples/inline-backticks.md: -------------------------------------------------------------------------------- 1 | `lots`of`backticks` 2 | 3 | ``i``wonder``how``this``will``be``parsed`` 4 | -------------------------------------------------------------------------------- /dingus/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !dingus.css 3 | !dingus.js 4 | !index.html 5 | !preview.html 6 | !bower.json 7 | !Makefile 8 | -------------------------------------------------------------------------------- /bench/samples/block-hr.md: -------------------------------------------------------------------------------- 1 | 2 | * * * * * 3 | 4 | - - - - - 5 | 6 | ________ 7 | 8 | 9 | ************************* text 10 | 11 | -------------------------------------------------------------------------------- /bench/samples/block-code.md: -------------------------------------------------------------------------------- 1 | 2 | an 3 | example 4 | 5 | of 6 | 7 | 8 | 9 | a code 10 | block 11 | 12 | -------------------------------------------------------------------------------- /bench/samples/block-fences.md: -------------------------------------------------------------------------------- 1 | 2 | ``````````text 3 | an 4 | example 5 | ``` 6 | of 7 | 8 | 9 | a fenced 10 | ``` 11 | code 12 | block 13 | `````````` 14 | 15 | -------------------------------------------------------------------------------- /bench/samples/block-lheading.md: -------------------------------------------------------------------------------- 1 | heading 2 | --- 3 | 4 | heading 5 | =================================== 6 | 7 | not a heading 8 | ----------------------------------- text 9 | -------------------------------------------------------------------------------- /bench/samples/block-heading.md: -------------------------------------------------------------------------------- 1 | # heading 2 | ### heading 3 | ##### heading 4 | 5 | # heading # 6 | ### heading ### 7 | ##### heading \#\#\#\#\###### 8 | 9 | ############ not a heading 10 | -------------------------------------------------------------------------------- /bench/samples/inline-em-flat.md: -------------------------------------------------------------------------------- 1 | *this* *is* *your* *basic* *boring* *emphasis* 2 | 3 | _this_ _is_ _your_ _basic_ _boring_ _emphasis_ 4 | 5 | **this** **is** **your** **basic** **boring** **emphasis** 6 | -------------------------------------------------------------------------------- /bench/samples/inline-em-nested.md: -------------------------------------------------------------------------------- 1 | *this *is *a *bunch* of* nested* emphases* 2 | 3 | __this __is __a __bunch__ of__ nested__ emphases__ 4 | 5 | ***this ***is ***a ***bunch*** of*** nested*** emphases*** 6 | -------------------------------------------------------------------------------- /bench/samples/inline-em-worst.md: -------------------------------------------------------------------------------- 1 | *this *is *a *worst *case *for *em *backtracking 2 | 3 | __this __is __a __worst __case __for __em __backtracking 4 | 5 | ***this ***is ***a ***worst ***case ***for ***em ***backtracking 6 | -------------------------------------------------------------------------------- /release_checklist.md: -------------------------------------------------------------------------------- 1 | Release checklist 2 | 3 | _ update changelog.txt 4 | _ update version in package.json 5 | _ test 6 | _ tag release 7 | _ git push 8 | _ git push --tags 9 | _ npm login 10 | _ npm publish 11 | _ create github release 12 | -------------------------------------------------------------------------------- /dingus/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "commonmark-dingus", 3 | "description": "CommonMark dingus", 4 | "private": true, 5 | "dependencies": { 6 | "bootstrap": "^3.4.1", 7 | "jquery": "^3.1.1", 8 | "lodash": "^4.17.19" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /bench/samples/inline-escape.md: -------------------------------------------------------------------------------- 1 | 2 | \t\e\s\t\i\n\g \e\s\c\a\p\e \s\e\q\u\e\n\c\e\s 3 | 4 | \!\\\"\#\$\%\&\'\(\)\*\+\,\.\/\:\;\<\=\>\? 5 | 6 | \@ \[ \] \^ \_ \` \{ \| \} \~ \- \' 7 | 8 | \ 9 | \\ 10 | \\\ 11 | \\\\ 12 | \\\\\ 13 | 14 | \ \ \ \ 15 | 16 | -------------------------------------------------------------------------------- /bench/samples/inline-newlines.md: -------------------------------------------------------------------------------- 1 | 2 | this\ 3 | should\ 4 | be\ 5 | separated\ 6 | by\ 7 | newlines 8 | 9 | this 10 | should 11 | be 12 | separated 13 | by 14 | newlines 15 | too 16 | 17 | this 18 | should 19 | not 20 | be 21 | separated 22 | by 23 | newlines 24 | 25 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | end_of_line = lf 7 | charset = utf-8 8 | insert_final_newline = true 9 | 10 | [*.js] 11 | trim_trailing_whitespace = true 12 | indent_style = space 13 | indent_size = 4 14 | 15 | [Makefile] 16 | trim_trailing_whitespace = true 17 | indent_style = tab 18 | indent_size = 8 19 | -------------------------------------------------------------------------------- /bench/samples/block-bq-flat.md: -------------------------------------------------------------------------------- 1 | > the simple example of a blockquote 2 | > the simple example of a blockquote 3 | > the simple example of a blockquote 4 | > the simple example of a blockquote 5 | ... continuation 6 | ... continuation 7 | ... continuation 8 | ... continuation 9 | 10 | empty blockquote: 11 | 12 | > 13 | > 14 | > 15 | > 16 | 17 | -------------------------------------------------------------------------------- /bench/samples/block-ref-nested.md: -------------------------------------------------------------------------------- 1 | [[[[[[[foo]]]]]]] 2 | 3 | [[[[[[[foo]]]]]]]: bar 4 | [[[[[[foo]]]]]]: bar 5 | [[[[[foo]]]]]: bar 6 | [[[[foo]]]]: bar 7 | [[[foo]]]: bar 8 | [[foo]]: bar 9 | [foo]: bar 10 | 11 | [*[*[*[*[foo]*]*]*]*] 12 | 13 | [*[*[*[*[foo]*]*]*]*]: bar 14 | [*[*[*[foo]*]*]*]: bar 15 | [*[*[foo]*]*]: bar 16 | [*[foo]*]: bar 17 | [foo]: bar 18 | -------------------------------------------------------------------------------- /dingus/preview.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | commonmark.js preview 6 | 7 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /bench/samples/rawtabs.md: -------------------------------------------------------------------------------- 1 | 2 | this is a test for tab expansion, be careful not to replace them with spaces 3 | 4 | 1 4444 5 | 22 333 6 | 333 22 7 | 4444 1 8 | 9 | 10 | tab-indented line 11 | space-indented line 12 | tab-indented line 13 | 14 | 15 | a lot of spaces in between here 16 | 17 | a lot of tabs in between here 18 | 19 | -------------------------------------------------------------------------------- /bench/samples/inline-entity.md: -------------------------------------------------------------------------------- 1 | entities: 2 | 3 |   & © Æ Ď ¾ ℋ ⅆ ∲ 4 | 5 | # Ӓ Ϡ � 6 | 7 | non-entities: 8 | 9 | &18900987654321234567890; &1234567890098765432123456789009876543212345678987654; 10 | 11 | &qwertyuioppoiuytrewqwer; &oiuytrewqwertyuioiuytrewqwertyuioytrewqwertyuiiuytri; 12 | -------------------------------------------------------------------------------- /bench/samples/inline-links-nested.md: -------------------------------------------------------------------------------- 1 | Valid links: 2 | 3 | [[[[[[[[](test)](test)](test)](test)](test)](test)](test)] 4 | 5 | [ [[[[[[[[[[[[[[[[[[ [](test) ]]]]]]]]]]]]]]]]]] ](test) 6 | 7 | Invalid links: 8 | 9 | [[[[[[[[[ 10 | 11 | [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ 12 | 13 | ![![![![![![![![![![![![![![![![![![![![![![![![![![![![![![![![![![![![![![ 14 | -------------------------------------------------------------------------------- /bench/samples/block-bq-nested.md: -------------------------------------------------------------------------------- 1 | >>>>>> deeply nested blockquote 2 | >>>>> deeply nested blockquote 3 | >>>> deeply nested blockquote 4 | >>> deeply nested blockquote 5 | >> deeply nested blockquote 6 | > deeply nested blockquote 7 | 8 | > deeply nested blockquote 9 | >> deeply nested blockquote 10 | >>> deeply nested blockquote 11 | >>>> deeply nested blockquote 12 | >>>>> deeply nested blockquote 13 | >>>>>> deeply nested blockquote 14 | -------------------------------------------------------------------------------- /bench/samples/block-html.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | blah blah 4 | 5 |
6 | 7 | 8 | 9 | 12 | 13 |
10 | **test** 11 |
14 | 15 | 16 | 17 | 18 | 19 | 24 | 25 | 26 | 27 |
20 | 21 | test 22 | 23 |
28 | 29 | 32 | 33 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "commonmark", 3 | "main": "dist/commonmark.js", 4 | "homepage": "https://github.com/commonmark/commonmark.js", 5 | "description": "CommonMark parsing and rendering library", 6 | "license": "BSD-2-Clause", 7 | "ignore": [ 8 | "**/.*", 9 | "lib", 10 | "bin", 11 | "test", 12 | "tools", 13 | "bench", 14 | "dingus", 15 | "Makefile", 16 | "*.md", 17 | "release_checklist.md", 18 | ".eslintrc", 19 | "package.json" 20 | ], 21 | "keywords": ["markdown", "commonmark"] 22 | } 23 | -------------------------------------------------------------------------------- /bench/samples/block-ref-flat.md: -------------------------------------------------------------------------------- 1 | [1] [2] [3] [1] [2] [3] 2 | 3 | [looooooooooooooooooooooooooooooooooooooooooooooooooong label] 4 | 5 | [1]: 6 | [2]: http://something.example.com/foo/bar 'test' 7 | [3]: 8 | http://foo/bar 9 | [ looooooooooooooooooooooooooooooooooooooooooooooooooong label ]: 10 | 111 11 | 'test' 12 | [[[[[[[[[[[[[[[[[[[[ this should not slow down anything ]]]]]]]]]]]]]]]]]]]]: q 13 | (as long as it is not referenced anywhere) 14 | 15 | [[[[[[[[[[[[[[[[[[[[]: this is not a valid reference 16 | -------------------------------------------------------------------------------- /bench/samples/block-list-nested.md: -------------------------------------------------------------------------------- 1 | 2 | - this 3 | - is 4 | - a 5 | - deeply 6 | - nested 7 | - bullet 8 | - list 9 | 10 | 11 | 1. this 12 | 2. is 13 | 3. a 14 | 4. deeply 15 | 5. nested 16 | 6. unordered 17 | 7. list 18 | 19 | 20 | - 1 21 | - 2 22 | - 3 23 | - 4 24 | - 5 25 | - 6 26 | - 7 27 | - 6 28 | - 5 29 | - 4 30 | - 3 31 | - 2 32 | - 1 33 | 34 | 35 | - - - - - - - - - deeply-nested one-element item 36 | 37 | -------------------------------------------------------------------------------- /bench/samples/inline-autolink.md: -------------------------------------------------------------------------------- 1 | closed (valid) autolinks: 2 | 3 | 4 | 5 | 6 | 7 | 8 | these are not autolinks: 9 | 10 | 15 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | // commonmark.js - CommonMark in JavaScript 4 | // Copyright (C) 2014 John MacFarlane 5 | // License: BSD3. 6 | 7 | // Basic usage: 8 | // 9 | // import { Parser, HtmlRenderer } from 'commonmark'; 10 | // var parser = new Parser(); 11 | // var renderer = new HtmlRenderer(); 12 | // console.log(renderer.render(parser.parse('Hello *world*'))); 13 | 14 | export { default as Node } from "./node.js"; 15 | export { default as Parser } from "./blocks.js"; 16 | export { default as Renderer } from "./render/renderer.js"; 17 | export { default as HtmlRenderer } from "./render/html.js"; 18 | export { default as XmlRenderer } from "./render/xml.js"; 19 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": false, 4 | "node": true 5 | }, 6 | "parserOptions": { 7 | "sourceType": "module", 8 | "ecmaVersion": 2018 9 | }, 10 | "rules": { 11 | "no-unused-vars": [ 12 | "error", 13 | { "vars": "all", "args": "after-used", "ignoreRestSiblings": false } 14 | ], 15 | "no-constant-condition": 0, 16 | "no-underscore-dangle": 0, 17 | "camelcase": 0, 18 | "quotes": 0, 19 | "no-process-exit": 0, 20 | "no-empty": 0, 21 | "new-cap": [ 22 | 2, 23 | { 24 | "newIsCap": true, 25 | "capIsNew": true, 26 | "newIsCapExceptions": [], 27 | "capIsNewExceptions": [] 28 | } 29 | ] 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI tests 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | 7 | linux: 8 | runs-on: ubuntu-latest 9 | strategy: 10 | fail-fast: false 11 | matrix: 12 | versions: 13 | - node: '17.x' 14 | - node: '16.x' 15 | - node: '15.x' 16 | - node: '14.x' 17 | - node: '13.x' 18 | - node: '12.x' 19 | steps: 20 | - uses: actions/checkout@v1 21 | - name: Use Node.js 22 | uses: actions/setup-node@v1 23 | with: 24 | node-version: ${{ matrix.versions.node }} 25 | - run: npm install 26 | - run: npm run lint 27 | - run: npm run build --if-present 28 | - run: npm test 29 | -------------------------------------------------------------------------------- /bench/samples/inline-html.md: -------------------------------------------------------------------------------- 1 | Taking commonmark tests from the spec for benchmarking here: 2 | 3 | 4 | 5 | 6 | 7 | 9 | 10 | 12 | 13 | <33> <__> 14 | 15 | 16 | 17 | 28 | 29 | foo 31 | 32 | foo 33 | 34 | foo 35 | 36 | foo 37 | 38 | foo &<]]> 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /bench/samples/inline-links-flat.md: -------------------------------------------------------------------------------- 1 | Valid links: 2 | 3 | [this is a link]() 4 | [this is a link]() 5 | [this is a link](http://something.example.com/foo/bar 'test') 6 | ![this is an image]() 7 | ![this is an image]() 8 | ![this is an image](http://something.example.com/foo/bar 'test') 9 | 10 | [escape test](<\>\>\>\>\>\>\>\>\>\>\>\>\>\>> '\'\'\'\'\'\'\'\'\'\'\'\'\'\'') 11 | [escape test \]\]\]\]\]\]\]\]\]\]\]\]\]\]\]\]](\)\)\)\)\)\)\)\)\)\)\)\)\)\)) 12 | 13 | Invalid links: 14 | 15 | [this is not a link 16 | 17 | [this is not a link]( 18 | 19 | [this is not a link](http://something.example.com/foo/bar 'test' 20 | 21 | [this is not a link]((((((((((((((((((((((((((((((((((((((((((((((( 22 | 23 | [this is not a link]((((((((((()))))))))) (((((((((())))))))))) 24 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | SPEC=test/spec.txt 2 | SPECVERSION=$(shell perl -ne 'print $$1 if /^version: *([0-9.]+)/' $(SPEC)) 3 | BENCHINP?=bench/samples/README.md 4 | VERSION?=$(SPECVERSION) 5 | JSMODULES=$(wildcard lib/*.js) 6 | LICENSETEXT="/* commonmark $(VERSION) https://github.com/commonmark/commonmark.js @license BSD3 */" 7 | 8 | .PHONY: dingus test bench bench-detailed npm lint clean update-spec 9 | 10 | lint: 11 | npm run lint 12 | 13 | update-spec: 14 | curl 'https://raw.githubusercontent.com/jgm/CommonMark/master/spec.txt' > $(SPEC) 15 | 16 | test: $(SPEC) 17 | npm test 18 | 19 | bench: 20 | node bench/bench.js ${BENCHINP} 21 | 22 | bench-detailed: 23 | sudo renice -10 $$$$; \ 24 | for x in bench/samples/*.md; do echo $$x; node bench/bench.js $$x; done | \ 25 | awk -f bench/format_benchmarks.awk 26 | 27 | dingus: 28 | make -C dingus dingus 29 | 30 | clean: 31 | -------------------------------------------------------------------------------- /bench/samples/block-list-flat.md: -------------------------------------------------------------------------------- 1 | - tidy 2 | - bullet 3 | - list 4 | 5 | 6 | - loose 7 | 8 | - bullet 9 | 10 | - list 11 | 12 | 13 | 0. ordered 14 | 1. list 15 | 2. example 16 | 17 | 18 | - 19 | - 20 | - 21 | - 22 | 23 | 24 | 1. 25 | 2. 26 | 3. 27 | 28 | 29 | - an example 30 | of a list item 31 | with a continuation 32 | 33 | this part is inside the list 34 | 35 | this part is just a paragraph 36 | 37 | 38 | 1. test 39 | - test 40 | 1. test 41 | - test 42 | 43 | 44 | 111111111111111111111111111111111111111111. is this a valid bullet? 45 | 46 | - _________________________ 47 | 48 | - this 49 | - is 50 | 51 | a 52 | 53 | long 54 | - loose 55 | - list 56 | 57 | - with 58 | - some 59 | 60 | tidy 61 | 62 | - list 63 | - items 64 | - in 65 | 66 | - between 67 | - _________________________ 68 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | // rollup.config.js 2 | import nodeResolve from "@rollup/plugin-node-resolve"; 3 | import commonjs from "@rollup/plugin-commonjs"; 4 | import json from "@rollup/plugin-json"; 5 | import { uglify } from "rollup-plugin-uglify"; 6 | import { version } from './package.json'; 7 | 8 | var banner = "/* commonmark " + version + " https://github.com/commonmark/commonmark.js @license BSD3 */"; 9 | 10 | export default { 11 | input: "lib/index.js", 12 | output: [ 13 | { 14 | file: "dist/commonmark.js", 15 | format: "umd", 16 | name: "commonmark", 17 | banner: banner, 18 | }, 19 | { 20 | file: "dist/commonmark.min.js", 21 | format: "umd", 22 | name: "commonmark", 23 | banner: banner, 24 | plugins: [uglify()] 25 | } 26 | ], 27 | plugins: [nodeResolve(), commonjs(), json()] 28 | }; 29 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Contributing 2 | ============ 3 | 4 | Submitting bug reports 5 | ---------------------- 6 | 7 | There is a [forum for discussing CommonMark](http://talk.commonmark.org); 8 | use it for questions and discussions that might be open-ended. Use the 9 | [github issue tracker](http://github.com/commonmark/commonmark.js/issues) 10 | only for simple, clear, actionable issues. 11 | 12 | Submitting pull requests 13 | ------------------------ 14 | 15 | 1. A good pull request makes one logical change and does not mix 16 | several independent changes. 17 | 2. If you have several commits that successively refine a single 18 | logical change, rebase them into a single clean commit. 19 | 3. Ensure that all tests pass (`make test`). 20 | 4. Use `make lint` to check for problems. 21 | 5. Ensure that performance has not been affected (`make bench` before 22 | and after the change). 23 | 6. Changes to `dist` should not be committed. (We will regenerate 24 | `dist/commonmark.js` before a release.) 25 | 7. Follow the style of the existing code. 26 | -------------------------------------------------------------------------------- /dingus/dingus.css: -------------------------------------------------------------------------------- 1 | h1.title { font-family: monospace; font-size: 120%; font-weight: bold; margin-top: 0.5em; margin-bottom: 0; } 2 | pre#htmlpre { height: 400px; overflow: scroll; resize: vertical; width: 100%; } 3 | pre#astpre { height: 400px; overflow: scroll; resize: vertical; width: 100%; } 4 | div#preview { height: 400px; resize: vertical; width: 100%; } 5 | iframe{ height: 400px; width: 100%; border: none; padding: 0; margin: 0; overflow: scroll; } 6 | div.row { margin-top: 1em; } 7 | blockquote { font-size: 100%; } 8 | footer { color: #555; text-align: center; margin: 1em; } 9 | pre { display: block; padding: 0.5em; color: #333; background: #f8f8ff } 10 | #warnings li { color: red; font-weight: bold; } 11 | label { padding-left: 1em; padding-top: 0; padding-bottom: 0; } 12 | div.timing { color: gray; visibility: hidden; height: 2em; } 13 | p#text-controls { height: 1em; margin-top: 1em; } 14 | a#permalink { margin-left: 1em; } 15 | span.timing { font-weight: bold; } 16 | .selected { background-color: #eeeeee; } 17 | textarea#text { width: 100%; overflow: scroll; resize: vertical; height: 400px; font-family: monospace; white-space: pre; word-wrap: normal; background-color: white; color: black; } 18 | -------------------------------------------------------------------------------- /dingus/Makefile: -------------------------------------------------------------------------------- 1 | ALL=commonmark.js 2 | COMPONENTS=lodash.min.js jquery.min.js jquery.min.map bootstrap.min.js bootstrap.min.css 3 | LIBFILES=../lib/blocks.js ../lib/common.js ../lib/from-code-point.js ../lib/inlines.js ../lib/index.js ../lib/node.js ../lib/render/xml.js ../lib/render/html.js ../lib/render/renderer.js 4 | BOWER=../node_modules/.bin/bower 5 | HTTPSERVER=../node_modules/.bin/http-server 6 | 7 | all: $(ALL) components $(COMPONENTS) 8 | 9 | .PHONY: all dingus components clean 10 | 11 | components: 12 | $(BOWER) install 13 | 14 | lodash.min.js: bower_components/lodash 15 | cp $=8.7.0", 53 | "benchmark": "^2.1.4", 54 | "bower": "^1.8.13", 55 | "cached-path-relative": "^1.0.2", 56 | "eslint": "^8.6.0", 57 | "http-server": "^14.1.0", 58 | "lodash": "^4.17.21", 59 | "markdown-it": ">= 12.3", 60 | "marked": ">=4.0.10", 61 | "mem": ">=4.0.0", 62 | "rollup": "^1.29.0", 63 | "rollup-plugin-uglify": "^6.0.4", 64 | "serialize-javascript": "^6.0.0", 65 | "showdown": "^1.9.1", 66 | "uglify-js": "^3.14.5" 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /dingus/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | commonmark.js demo 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 |
16 |
17 |

commonmark.js dingus

18 |
19 |
20 |
21 |
22 |

clear     24 | 25 | 26 |

27 | 39 |
    40 |
    41 |
    42 | 47 |
    48 |
    49 | 50 |
    51 |
    52 |
    53 |
    54 |
    55 |
    56 |
    57 |
    58 |
    Parsed in 59 | ms. Rendered in ms.
    60 |
    61 |
    62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /lib/from-code-point.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | // derived from https://github.com/mathiasbynens/String.fromCodePoint 4 | /*! http://mths.be/fromcodepoint v0.2.1 by @mathias */ 5 | 6 | var _fromCodePoint; 7 | 8 | export default function fromCodePoint(_) { 9 | return _fromCodePoint(_); 10 | } 11 | 12 | if (String.fromCodePoint) { 13 | _fromCodePoint = function(_) { 14 | try { 15 | return String.fromCodePoint(_); 16 | } catch (e) { 17 | if (e instanceof RangeError) { 18 | return String.fromCharCode(0xfffd); 19 | } 20 | throw e; 21 | } 22 | }; 23 | } else { 24 | var stringFromCharCode = String.fromCharCode; 25 | var floor = Math.floor; 26 | _fromCodePoint = function() { 27 | var MAX_SIZE = 0x4000; 28 | var codeUnits = []; 29 | var highSurrogate; 30 | var lowSurrogate; 31 | var index = -1; 32 | var length = arguments.length; 33 | if (!length) { 34 | return ""; 35 | } 36 | var result = ""; 37 | while (++index < length) { 38 | var codePoint = Number(arguments[index]); 39 | if ( 40 | !isFinite(codePoint) || // `NaN`, `+Infinity`, or `-Infinity` 41 | codePoint < 0 || // not a valid Unicode code point 42 | codePoint > 0x10ffff || // not a valid Unicode code point 43 | floor(codePoint) !== codePoint // not an integer 44 | ) { 45 | return String.fromCharCode(0xfffd); 46 | } 47 | if (codePoint <= 0xffff) { 48 | // BMP code point 49 | codeUnits.push(codePoint); 50 | } else { 51 | // Astral code point; split in surrogate halves 52 | // http://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae 53 | codePoint -= 0x10000; 54 | highSurrogate = (codePoint >> 10) + 0xd800; 55 | lowSurrogate = (codePoint % 0x400) + 0xdc00; 56 | codeUnits.push(highSurrogate, lowSurrogate); 57 | } 58 | if (index + 1 === length || codeUnits.length > MAX_SIZE) { 59 | result += stringFromCharCode.apply(null, codeUnits); 60 | codeUnits.length = 0; 61 | } 62 | } 63 | return result; 64 | }; 65 | } 66 | -------------------------------------------------------------------------------- /bin/commonmark: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | "use strict"; 3 | 4 | var util = require('util'); 5 | var fs = require('fs'); 6 | var os = require('os'); 7 | var commonmark = require('../'); 8 | var version = require('../package.json').version; 9 | var parseArgs = require('minimist'); 10 | var args = process.argv.slice(2); 11 | var argv = parseArgs(args, 12 | {boolean: ["ast", "xml", "time", "smart", 13 | "safe", "sourcepos", "help", "version"], 14 | string: ["_", "to", "t"], 15 | unknown: function(o) { 16 | if (/^-/.test(o)) { 17 | process.stderr.write("Unknown option " + o + "\n"); 18 | process.exit(1); 19 | } 20 | } 21 | }); 22 | 23 | var inps = []; 24 | var i; 25 | 26 | var usage = function() { 27 | process.stdout.write("commonmark [options] [file..]\n\n" + 28 | "Convert CommonMark to HTML. Files are concatenated before\n" + 29 | "parsing. If no files are given, input is read from stdin.\n\n" + 30 | "-t,--to FORMAT Specify output format (xml, html, ast)\n" + 31 | "--time Produce timings for phases of parsing and rendering\n" + 32 | "--smart Parse 'smart' punctuation\n" + 33 | "--safe Omit raw HTML and potentially unsafe attributes\n" + 34 | "--sourcepos Include source position attributes in HTML tags\n" + 35 | "--version Print version information\n" + 36 | "--help Print usage information\n"); 37 | process.exit(0); 38 | }; 39 | 40 | if (argv.help) { 41 | usage(); 42 | process.exit(0); 43 | } 44 | if (argv.version) { 45 | process.stdout.write('commonmark.js ' + version + '\n'); 46 | process.exit(0); 47 | } 48 | 49 | var format = argv.to || argv.t || (argv.xml ? 'xml' : 50 | (argv.ast ? 'ast' : 'html')); 51 | 52 | var options = { 53 | time: argv.time, 54 | smart: argv.smart, 55 | safe: argv.safe, 56 | sourcepos: argv.sourcepos 57 | }; 58 | 59 | var files = argv._; 60 | 61 | var parser = new commonmark.Parser(options); 62 | var renderer; 63 | 64 | if (format === 'html') { 65 | renderer = new commonmark.HtmlRenderer(options); 66 | } else if (format === 'xml') { 67 | renderer = new commonmark.XmlRenderer(options); 68 | } else if (format === 'ast') { 69 | renderer = { render: function(node) { 70 | return util.inspect(node, null, 20, true) + '\n'; 71 | }, 72 | options: {} }; 73 | } else { 74 | process.stderr.write("Unknown format: " + format + "\n"); 75 | process.exit(1); 76 | } 77 | 78 | if (files.length === 0) { 79 | files = ['/dev/stdin']; 80 | } 81 | 82 | for (i = 0; i < files.length; i++) { 83 | var file = files[i]; 84 | if (file === "/dev/stdin" && os.platform() === "win32") { 85 | file = 0; 86 | } 87 | inps.push(fs.readFileSync(file, 'utf8')); 88 | } 89 | 90 | var inp = inps.join('\n'); 91 | var doc = parser.parse(inp); 92 | 93 | var rendered = renderer.render(doc); 94 | 95 | if (!options.time) { process.stdout.write(rendered); } 96 | -------------------------------------------------------------------------------- /lib/common.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | import encode from "mdurl/encode.js"; 4 | import { decodeHTMLStrict } from "entities"; 5 | 6 | var C_BACKSLASH = 92; 7 | 8 | var ENTITY = "&(?:#x[a-f0-9]{1,6}|#[0-9]{1,7}|[a-z][a-z0-9]{1,31});"; 9 | 10 | var TAGNAME = "[A-Za-z][A-Za-z0-9-]*"; 11 | var ATTRIBUTENAME = "[a-zA-Z_:][a-zA-Z0-9:._-]*"; 12 | var UNQUOTEDVALUE = "[^\"'=<>`\\x00-\\x20]+"; 13 | var SINGLEQUOTEDVALUE = "'[^']*'"; 14 | var DOUBLEQUOTEDVALUE = '"[^"]*"'; 15 | var ATTRIBUTEVALUE = 16 | "(?:" + 17 | UNQUOTEDVALUE + 18 | "|" + 19 | SINGLEQUOTEDVALUE + 20 | "|" + 21 | DOUBLEQUOTEDVALUE + 22 | ")"; 23 | var ATTRIBUTEVALUESPEC = "(?:" + "\\s*=" + "\\s*" + ATTRIBUTEVALUE + ")"; 24 | var ATTRIBUTE = "(?:" + "\\s+" + ATTRIBUTENAME + ATTRIBUTEVALUESPEC + "?)"; 25 | var OPENTAG = "<" + TAGNAME + ATTRIBUTE + "*" + "\\s*/?>"; 26 | var CLOSETAG = "]"; 27 | var HTMLCOMMENT = "||" 28 | var PROCESSINGINSTRUCTION = "[<][?][\\s\\S]*?[?][>]"; 29 | var DECLARATION = "]*>"; 30 | var CDATA = ""; 31 | var HTMLTAG = 32 | "(?:" + 33 | OPENTAG + 34 | "|" + 35 | CLOSETAG + 36 | "|" + 37 | HTMLCOMMENT + 38 | "|" + 39 | PROCESSINGINSTRUCTION + 40 | "|" + 41 | DECLARATION + 42 | "|" + 43 | CDATA + 44 | ")"; 45 | var reHtmlTag = new RegExp("^" + HTMLTAG); 46 | 47 | var reBackslashOrAmp = /[\\&]/; 48 | 49 | var ESCAPABLE = "[!\"#$%&'()*+,./:;<=>?@[\\\\\\]^_`{|}~-]"; 50 | 51 | var reEntityOrEscapedChar = new RegExp("\\\\" + ESCAPABLE + "|" + ENTITY, "gi"); 52 | 53 | var XMLSPECIAL = '[&<>"]'; 54 | 55 | var reXmlSpecial = new RegExp(XMLSPECIAL, "g"); 56 | 57 | var unescapeChar = function(s) { 58 | if (s.charCodeAt(0) === C_BACKSLASH) { 59 | return s.charAt(1); 60 | } else { 61 | return decodeHTMLStrict(s); 62 | } 63 | }; 64 | 65 | // Replace entities and backslash escapes with literal characters. 66 | var unescapeString = function(s) { 67 | if (reBackslashOrAmp.test(s)) { 68 | return s.replace(reEntityOrEscapedChar, unescapeChar); 69 | } else { 70 | return s; 71 | } 72 | }; 73 | 74 | var normalizeURI = function(uri) { 75 | try { 76 | return encode(uri); 77 | } catch (err) { 78 | return uri; 79 | } 80 | }; 81 | 82 | var replaceUnsafeChar = function(s) { 83 | switch (s) { 84 | case "&": 85 | return "&"; 86 | case "<": 87 | return "<"; 88 | case ">": 89 | return ">"; 90 | case '"': 91 | return """; 92 | default: 93 | return s; 94 | } 95 | }; 96 | 97 | var escapeXml = function(s) { 98 | if (reXmlSpecial.test(s)) { 99 | return s.replace(reXmlSpecial, replaceUnsafeChar); 100 | } else { 101 | return s; 102 | } 103 | }; 104 | 105 | export { 106 | unescapeString, 107 | normalizeURI, 108 | escapeXml, 109 | reHtmlTag, 110 | OPENTAG, 111 | CLOSETAG, 112 | ENTITY, 113 | ESCAPABLE 114 | }; 115 | -------------------------------------------------------------------------------- /bench/samples/lorem1.md: -------------------------------------------------------------------------------- 1 | Lorem ipsum dolor sit amet, __consectetur__ adipiscing elit. Cras imperdiet nec erat ac condimentum. Nulla vel rutrum ligula. Sed hendrerit interdum orci a posuere. Vivamus ut velit aliquet, mollis purus eget, iaculis nisl. Proin posuere malesuada ante. Proin auctor orci eros, ac molestie lorem dictum nec. Vestibulum sit amet erat est. Morbi luctus sed elit ac luctus. Proin blandit, enim vitae egestas posuere, neque elit ultricies dui, vel mattis nibh enim ac lorem. Maecenas molestie nisl sit amet velit dictum lobortis. Aliquam erat volutpat. 2 | 3 | Vivamus sagittis, diam in [vehicula](https://github.com/markdown-it/markdown-it) lobortis, sapien arcu mattis erat, vel aliquet sem urna et risus. Ut feugiat sapien vitae mi elementum laoreet. Suspendisse potenti. Aliquam erat nisl, aliquam pretium libero aliquet, sagittis eleifend nunc. In hac habitasse platea dictumst. Integer turpis augue, tincidunt dignissim mauris id, rhoncus dapibus purus. Maecenas et enim odio. Nullam massa metus, varius quis vehicula sed, pharetra mollis erat. In quis viverra velit. Vivamus placerat, est nec hendrerit varius, enim dui hendrerit magna, ut pulvinar nibh lorem vel lacus. Mauris a orci iaculis, hendrerit eros sed, gravida leo. In dictum mauris vel augue varius, ac ullamcorper nisl ornare. In eu posuere velit, ac fermentum arcu. Interdum et malesuada fames ac ante ipsum primis in faucibus. Nullam sed malesuada leo, at interdum elit. 4 | 5 | Nullam ut tincidunt nunc. [Pellentesque][1] metus lacus, commodo eget justo ut, rutrum varius nunc. Sed non rhoncus risus. Morbi sodales gravida pulvinar. Duis malesuada, odio volutpat elementum vulputate, massa magna scelerisque ante, et accumsan tellus nunc in sem. Donec mattis arcu et velit aliquet, non sagittis justo vestibulum. Suspendisse volutpat felis lectus, nec consequat ipsum mattis id. Donec dapibus vehicula facilisis. In tincidunt mi nisi, nec faucibus tortor euismod nec. Suspendisse ante ligula, aliquet vitae libero eu, vulputate dapibus libero. Sed bibendum, sapien at posuere interdum, libero est sollicitudin magna, ac gravida tellus purus eu ipsum. Proin ut quam arcu. 6 | 7 | Suspendisse potenti. Donec ante velit, ornare at augue quis, tristique laoreet sem. Etiam in ipsum elit. Nullam cursus dolor sit amet nulla feugiat tristique. Phasellus ac tellus tincidunt, imperdiet purus eget, ullamcorper ipsum. Cras eu tincidunt sem. Nullam sed dapibus magna. Lorem ipsum dolor sit amet, consectetur adipiscing elit. In id venenatis tortor. In consectetur sollicitudin pharetra. Etiam convallis nisi nunc, et aliquam turpis viverra sit amet. Maecenas faucibus sodales tortor. Suspendisse lobortis mi eu leo viverra volutpat. Pellentesque velit ante, vehicula sodales congue ut, elementum a urna. Cras tempor, ipsum eget luctus rhoncus, arcu ligula fermentum urna, vulputate pharetra enim enim non libero. 8 | 9 | Proin diam quam, elementum in eleifend id, elementum et metus. Cras in justo consequat justo semper ultrices. Sed dignissim lectus a ante mollis, nec vulputate ante molestie. Proin in porta nunc. Etiam pulvinar turpis sed velit porttitor, vel adipiscing velit fringilla. Cras ac tellus vitae purus pharetra tincidunt. Sed cursus aliquet aliquet. Cras eleifend commodo malesuada. In turpis turpis, ullamcorper ut tincidunt a, ullamcorper a nunc. Etiam luctus tellus ac dapibus gravida. Ut nec lacus laoreet neque ullamcorper volutpat. 10 | 11 | Nunc et leo erat. Aenean mattis ultrices lorem, eget adipiscing dolor ultricies eu. In hac habitasse platea dictumst. Vivamus cursus feugiat sapien quis aliquam. Mauris quam libero, porta vel volutpat ut, blandit a purus. Vivamus vestibulum dui vel tortor molestie, sit amet feugiat sem commodo. Nulla facilisi. Sed molestie arcu eget tellus vestibulum tristique. 12 | 13 | [1]: https://github.com/markdown-it 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014, John MacFarlane 2 | 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above 12 | copyright notice, this list of conditions and the following 13 | disclaimer in the documentation and/or other materials provided 14 | with the distribution. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | 28 | --- 29 | 30 | lib/from-code-point.js is derived from a polyfill 31 | Copyright Mathias Bynens 32 | 33 | Permission is hereby granted, free of charge, to any person obtaining 34 | a copy of this software and associated documentation files (the 35 | "Software"), to deal in the Software without restriction, including 36 | without limitation the rights to use, copy, modify, merge, publish, 37 | distribute, sublicense, and/or sell copies of the Software, and to 38 | permit persons to whom the Software is furnished to do so, subject to 39 | the following conditions: 40 | 41 | The above copyright notice and this permission notice shall be 42 | included in all copies or substantial portions of the Software. 43 | 44 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 45 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 46 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 47 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 48 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 49 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 50 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 51 | 52 | --- 53 | 54 | bench/samples/*.md: 55 | 56 | With the exception of `bench/samples/README.md`, the samples in 57 | `bench/samples` are taken from https://github.com/markdown-it/markdown-it. 58 | 59 | Copyright (c) 2014 Vitaly Puzrin, Alex Kocharin. 60 | 61 | Permission is hereby granted, free of charge, to any person 62 | obtaining a copy of this software and associated documentation 63 | files (the "Software"), to deal in the Software without 64 | restriction, including without limitation the rights to use, 65 | copy, modify, merge, publish, distribute, sublicense, and/or sell 66 | copies of the Software, and to permit persons to whom the 67 | Software is furnished to do so, subject to the following 68 | conditions: 69 | 70 | The above copyright notice and this permission notice shall be 71 | included in all copies or substantial portions of the Software. 72 | 73 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 74 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 75 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 76 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 77 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 78 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 79 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 80 | OTHER DEALINGS IN THE SOFTWARE. 81 | 82 | --- 83 | 84 | The CommonMark spec (spec.txt) in test/ is 85 | 86 | Copyright (C) 2014-15 John MacFarlane 87 | 88 | Released under the Creative Commons CC-BY-SA 4.0 license: 89 | . 90 | -------------------------------------------------------------------------------- /test/smart_punct.txt: -------------------------------------------------------------------------------- 1 | ## Smart punctuation 2 | 3 | Open quotes are matched with closed quotes. 4 | The same method is used for matching openers and closers 5 | as is used in emphasis parsing: 6 | 7 | ```````````````````````````````` example 8 | "Hello," said the spider. 9 | "'Shelob' is my name." 10 | . 11 |

    “Hello,” said the spider. 12 | “‘Shelob’ is my name.”

    13 | ```````````````````````````````` 14 | 15 | ```````````````````````````````` example 16 | 'A', 'B', and 'C' are letters. 17 | . 18 |

    ‘A’, ‘B’, and ‘C’ are letters.

    19 | ```````````````````````````````` 20 | 21 | ```````````````````````````````` example 22 | 'Oak,' 'elm,' and 'beech' are names of trees. 23 | So is 'pine.' 24 | . 25 |

    ‘Oak,’ ‘elm,’ and ‘beech’ are names of trees. 26 | So is ‘pine.’

    27 | ```````````````````````````````` 28 | 29 | ```````````````````````````````` example 30 | 'He said, "I want to go."' 31 | . 32 |

    ‘He said, “I want to go.”’

    33 | ```````````````````````````````` 34 | 35 | A single quote that isn't an open quote matched 36 | with a close quote will be treated as an 37 | apostrophe: 38 | 39 | ```````````````````````````````` example 40 | Were you alive in the 70's? 41 | . 42 |

    Were you alive in the 70’s?

    43 | ```````````````````````````````` 44 | 45 | ```````````````````````````````` example 46 | Here is some quoted '`code`' and a "[quoted link](url)". 47 | . 48 |

    Here is some quoted ‘code’ and a “quoted link”.

    49 | ```````````````````````````````` 50 | 51 | Here the first `'` is treated as an apostrophe, not 52 | an open quote, because the final single quote is matched 53 | by the single quote before `jolly`: 54 | 55 | ```````````````````````````````` example 56 | 'tis the season to be 'jolly' 57 | . 58 |

    ’tis the season to be ‘jolly’

    59 | ```````````````````````````````` 60 | 61 | Multiple apostrophes should not be marked as open/closing quotes. 62 | 63 | ```````````````````````````````` example 64 | 'We'll use Jane's boat and John's truck,' Jenna said. 65 | . 66 |

    ‘We’ll use Jane’s boat and John’s truck,’ Jenna said.

    67 | ```````````````````````````````` 68 | 69 | An unmatched double quote will be interpreted as a 70 | left double quote, to facilitate this style: 71 | 72 | ```````````````````````````````` example 73 | "A paragraph with no closing quote. 74 | 75 | "Second paragraph by same speaker, in fiction." 76 | . 77 |

    “A paragraph with no closing quote.

    78 |

    “Second paragraph by same speaker, in fiction.”

    79 | ```````````````````````````````` 80 | 81 | Quotes that are escaped come out as literal straight 82 | quotes: 83 | 84 | ```````````````````````````````` example 85 | \"This is not smart.\" 86 | This isn\'t either. 87 | 5\'8\" 88 | . 89 |

    "This is not smart." 90 | This isn't either. 91 | 5'8"

    92 | ```````````````````````````````` 93 | 94 | Two hyphens form an en-dash, three an em-dash. 95 | 96 | ```````````````````````````````` example 97 | Some dashes: em---em 98 | en--en 99 | em --- em 100 | en -- en 101 | 2--3 102 | . 103 |

    Some dashes: em—em 104 | en–en 105 | em — em 106 | en – en 107 | 2–3

    108 | ```````````````````````````````` 109 | 110 | A sequence of more than three hyphens is 111 | parsed as a sequence of em and/or en dashes, 112 | with no hyphens. If possible, a homogeneous 113 | sequence of dashes is used (so, 10 hyphens 114 | = 5 en dashes, and 9 hyphens = 3 em dashes). 115 | When a heterogeneous sequence must be used, 116 | the em dashes come first, followed by the en 117 | dashes, and as few en dashes as possible are 118 | used (so, 7 hyphens = 2 em dashes an 1 en 119 | dash). 120 | 121 | ```````````````````````````````` example 122 | one- 123 | two-- 124 | three--- 125 | four---- 126 | five----- 127 | six------ 128 | seven------- 129 | eight-------- 130 | nine--------- 131 | thirteen-------------. 132 | . 133 |

    one- 134 | two– 135 | three— 136 | four–– 137 | five—– 138 | six—— 139 | seven—–– 140 | eight–––– 141 | nine——— 142 | thirteen———––.

    143 | ```````````````````````````````` 144 | 145 | Hyphens can be escaped: 146 | 147 | ```````````````````````````````` example 148 | Escaped hyphens: \-- \-\-\-. 149 | . 150 |

    Escaped hyphens: -- ---.

    151 | ```````````````````````````````` 152 | 153 | Three periods form an ellipsis: 154 | 155 | ```````````````````````````````` example 156 | Ellipses...and...and.... 157 | . 158 |

    Ellipses…and…and….

    159 | ```````````````````````````````` 160 | 161 | Periods can be escaped if ellipsis-formation 162 | is not wanted: 163 | 164 | ```````````````````````````````` example 165 | No ellipses\.\.\. 166 | . 167 |

    No ellipses...

    168 | ```````````````````````````````` 169 | -------------------------------------------------------------------------------- /dingus/dingus.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | /*eslint-env browser*/ 4 | /*global $, _ */ 5 | 6 | var commonmark = window.commonmark; 7 | var writer = new commonmark.HtmlRenderer({ sourcepos: true }); 8 | var htmlwriter = new commonmark.HtmlRenderer({ sourcepos: false }); 9 | var xmlwriter = new commonmark.XmlRenderer({ sourcepos: false }); 10 | var reader = new commonmark.Parser(); 11 | 12 | function getQueryVariable(variable) { 13 | var query = window.location.search.substring(1); 14 | var vars = query.split("&"); 15 | for (var i = 0; i < vars.length; i++) { 16 | var pair = vars[i].split("="); 17 | if (pair[0] === variable) { 18 | return decodeURIComponent(pair[1]); 19 | } 20 | } 21 | return null; 22 | } 23 | 24 | var render = function(parsed) { 25 | if (parsed === undefined) { 26 | return; 27 | } 28 | var startTime = new Date().getTime(); 29 | var result = writer.render(parsed); 30 | var endTime = new Date().getTime(); 31 | var renderTime = endTime - startTime; 32 | var preview = $("#preview iframe") 33 | .contents() 34 | .find("body"); 35 | preview.get(0).innerHTML = result; 36 | $("#html").text(htmlwriter.render(parsed)); 37 | $("#ast").text(xmlwriter.render(parsed)); 38 | $("#rendertime").text(renderTime); 39 | }; 40 | 41 | var syncScroll = function() { 42 | var textarea = $("#text"); 43 | var preview = $("#preview iframe") 44 | .contents() 45 | .find("body"); 46 | var lineHeight = parseFloat(textarea.css("line-height")); 47 | // NOTE this assumes we don't have wrapped lines, 48 | // so we have set white-space:nowrap on the textarea: 49 | var lineNumber = Math.floor(textarea.scrollTop() / lineHeight) + 1; 50 | var elt = preview.find("*[data-sourcepos^='" + lineNumber + ":']").last(); 51 | if (elt.length > 0) { 52 | if (elt.offset()) { 53 | preview.animate( 54 | { 55 | scrollTop: elt.offset().top - 100 56 | }, 57 | 50 58 | ); 59 | } 60 | } 61 | }; 62 | 63 | var markSelection = function() { 64 | var cursorPos = $("#text").prop("selectionStart"); 65 | // now count newline up to this pos 66 | var textval = $("#text").val(); 67 | var lineNumber = 1; 68 | for (var i = 0; i < cursorPos; i++) { 69 | if (textval.charAt(i) === "\n") { 70 | lineNumber++; 71 | } 72 | } 73 | var preview = $("#preview iframe") 74 | .contents() 75 | .find("body"); 76 | var elt = preview.find("[data-sourcepos^='" + lineNumber + ":']").last(); 77 | if (elt.length > 0) { 78 | preview.find(".selected").removeClass("selected"); 79 | elt.addClass("selected"); 80 | } 81 | syncScroll(); 82 | }; 83 | 84 | var parseAndRender = function() { 85 | var textarea = $("#text"); 86 | var startTime = new Date().getTime(); 87 | var parsed = reader.parse(textarea.val()); 88 | var endTime = new Date().getTime(); 89 | var parseTime = endTime - startTime; 90 | $("#parsetime").text(parseTime); 91 | $(".timing").css("visibility", "visible"); 92 | render(parsed); 93 | markSelection(); 94 | }; 95 | 96 | var onIframeLoad = function() { 97 | var textarea = $("#text"); 98 | var initial_text = getQueryVariable("text"); 99 | var smartSelected = getQueryVariable("smart") === "1"; 100 | $("#smart").prop("checked", smartSelected); 101 | reader.options.smart = smartSelected; 102 | if (initial_text) { 103 | textarea.val(initial_text); 104 | } 105 | 106 | parseAndRender(); 107 | 108 | $("#clear-text-box").click(function() { 109 | textarea.val(""); 110 | parseAndRender(); 111 | }); 112 | 113 | $("#permalink").click(function() { 114 | var smart = $("#smart").prop("checked"); 115 | window.location.pathname = "/index.html"; 116 | window.location.search = 117 | "text=" + 118 | encodeURIComponent(textarea.val()) + 119 | (smart ? "&smart=1" : ""); 120 | }); 121 | 122 | textarea.bind( 123 | "input propertychange", 124 | _.debounce(parseAndRender, 50, { maxWait: 100 }) 125 | ); 126 | //textarea.on('scroll', _.debounce(syncScroll, 50, { maxWait: 50 })); 127 | textarea.on("scroll", syncScroll); 128 | textarea.on( 129 | "keydown click focus", 130 | _.debounce(markSelection, 50, { maxWait: 100 }) 131 | ); 132 | 133 | $("#smart").click(function() { 134 | reader.options.smart = $("#smart").prop("checked"); 135 | parseAndRender(); 136 | }); 137 | }; 138 | 139 | var iframeLoaded = false; 140 | 141 | $("iframe").on("load", function() { 142 | iframeLoaded = true; 143 | }); 144 | 145 | $(document).ready(function() { 146 | if (iframeLoaded) { 147 | onIframeLoad(); 148 | } else { 149 | $("iframe").on("load", onIframeLoad); 150 | } 151 | }); 152 | -------------------------------------------------------------------------------- /lib/render/xml.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | import Renderer from "./renderer.js"; 4 | import { escapeXml } from "../common.js"; 5 | 6 | var reXMLTag = /\<[^>]*\>/; 7 | 8 | function toTagName(s) { 9 | return s.replace(/([a-z])([A-Z])/g, "$1_$2").toLowerCase(); 10 | } 11 | 12 | function XmlRenderer(options) { 13 | options = options || {}; 14 | 15 | this.disableTags = 0; 16 | this.lastOut = "\n"; 17 | 18 | this.indentLevel = 0; 19 | this.indent = " "; 20 | 21 | this.esc = options.esc || escapeXml; 22 | // escape html with a custom function 23 | // else use escapeXml 24 | 25 | this.options = options; 26 | } 27 | 28 | function render(ast) { 29 | this.buffer = ""; 30 | 31 | var attrs; 32 | var tagname; 33 | var walker = ast.walker(); 34 | var event, node, entering; 35 | var container; 36 | var selfClosing; 37 | var nodetype; 38 | 39 | var options = this.options; 40 | 41 | if (options.time) { 42 | console.time("rendering"); 43 | } 44 | 45 | this.buffer += '\n'; 46 | this.buffer += '\n'; 47 | 48 | while ((event = walker.next())) { 49 | entering = event.entering; 50 | node = event.node; 51 | nodetype = node.type; 52 | 53 | container = node.isContainer; 54 | 55 | selfClosing = 56 | nodetype === "thematic_break" || 57 | nodetype === "linebreak" || 58 | nodetype === "softbreak"; 59 | 60 | tagname = toTagName(nodetype); 61 | 62 | if (entering) { 63 | attrs = []; 64 | 65 | switch (nodetype) { 66 | case "document": 67 | attrs.push(["xmlns", "http://commonmark.org/xml/1.0"]); 68 | break; 69 | case "list": 70 | if (node.listType !== null) { 71 | attrs.push(["type", node.listType.toLowerCase()]); 72 | } 73 | if (node.listStart !== null) { 74 | attrs.push(["start", String(node.listStart)]); 75 | } 76 | if (node.listTight !== null) { 77 | attrs.push([ 78 | "tight", 79 | node.listTight ? "true" : "false" 80 | ]); 81 | } 82 | var delim = node.listDelimiter; 83 | if (delim !== null) { 84 | var delimword = ""; 85 | if (delim === ".") { 86 | delimword = "period"; 87 | } else { 88 | delimword = "paren"; 89 | } 90 | attrs.push(["delimiter", delimword]); 91 | } 92 | break; 93 | case "code_block": 94 | if (node.info) { 95 | attrs.push(["info", node.info]); 96 | } 97 | break; 98 | case "heading": 99 | attrs.push(["level", String(node.level)]); 100 | break; 101 | case "link": 102 | case "image": 103 | attrs.push(["destination", node.destination]); 104 | attrs.push(["title", node.title]); 105 | break; 106 | case "custom_inline": 107 | case "custom_block": 108 | attrs.push(["on_enter", node.onEnter]); 109 | attrs.push(["on_exit", node.onExit]); 110 | break; 111 | default: 112 | break; 113 | } 114 | if (options.sourcepos) { 115 | var pos = node.sourcepos; 116 | if (pos) { 117 | attrs.push([ 118 | "sourcepos", 119 | String(pos[0][0]) + 120 | ":" + 121 | String(pos[0][1]) + 122 | "-" + 123 | String(pos[1][0]) + 124 | ":" + 125 | String(pos[1][1]) 126 | ]); 127 | } 128 | } 129 | 130 | this.cr(); 131 | this.out(this.tag(tagname, attrs, selfClosing)); 132 | if (container) { 133 | this.indentLevel += 1; 134 | } else if (!container && !selfClosing) { 135 | var lit = node.literal; 136 | if (lit) { 137 | this.out(this.esc(lit)); 138 | } 139 | this.out(this.tag("/" + tagname)); 140 | } 141 | } else { 142 | this.indentLevel -= 1; 143 | this.cr(); 144 | this.out(this.tag("/" + tagname)); 145 | } 146 | } 147 | if (options.time) { 148 | console.timeEnd("rendering"); 149 | } 150 | this.buffer += "\n"; 151 | return this.buffer; 152 | } 153 | 154 | function out(s) { 155 | if (this.disableTags > 0) { 156 | this.buffer += s.replace(reXMLTag, ""); 157 | } else { 158 | this.buffer += s; 159 | } 160 | this.lastOut = s; 161 | } 162 | 163 | function cr() { 164 | if (this.lastOut !== "\n") { 165 | this.buffer += "\n"; 166 | this.lastOut = "\n"; 167 | for (var i = this.indentLevel; i > 0; i--) { 168 | this.buffer += this.indent; 169 | } 170 | } 171 | } 172 | 173 | // Helper function to produce an XML tag. 174 | function tag(name, attrs, selfclosing) { 175 | var result = "<" + name; 176 | if (attrs && attrs.length > 0) { 177 | var i = 0; 178 | var attrib; 179 | while ((attrib = attrs[i]) !== undefined) { 180 | result += " " + attrib[0] + '="' + this.esc(attrib[1]) + '"'; 181 | i++; 182 | } 183 | } 184 | if (selfclosing) { 185 | result += " /"; 186 | } 187 | result += ">"; 188 | return result; 189 | } 190 | 191 | // quick browser-compatible inheritance 192 | XmlRenderer.prototype = Object.create(Renderer.prototype); 193 | 194 | XmlRenderer.prototype.render = render; 195 | XmlRenderer.prototype.out = out; 196 | XmlRenderer.prototype.cr = cr; 197 | XmlRenderer.prototype.tag = tag; 198 | XmlRenderer.prototype.esc = escapeXml; 199 | 200 | export default XmlRenderer; 201 | -------------------------------------------------------------------------------- /lib/node.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | function isContainer(node) { 4 | switch (node._type) { 5 | case "document": 6 | case "block_quote": 7 | case "list": 8 | case "item": 9 | case "paragraph": 10 | case "heading": 11 | case "emph": 12 | case "strong": 13 | case "link": 14 | case "image": 15 | case "custom_inline": 16 | case "custom_block": 17 | return true; 18 | default: 19 | return false; 20 | } 21 | } 22 | 23 | var resumeAt = function(node, entering) { 24 | this.current = node; 25 | this.entering = entering === true; 26 | }; 27 | 28 | var next = function() { 29 | var cur = this.current; 30 | var entering = this.entering; 31 | 32 | if (cur === null) { 33 | return null; 34 | } 35 | 36 | var container = isContainer(cur); 37 | 38 | if (entering && container) { 39 | if (cur._firstChild) { 40 | this.current = cur._firstChild; 41 | this.entering = true; 42 | } else { 43 | // stay on node but exit 44 | this.entering = false; 45 | } 46 | } else if (cur === this.root) { 47 | this.current = null; 48 | } else if (cur._next === null) { 49 | this.current = cur._parent; 50 | this.entering = false; 51 | } else { 52 | this.current = cur._next; 53 | this.entering = true; 54 | } 55 | 56 | return { entering: entering, node: cur }; 57 | }; 58 | 59 | var NodeWalker = function(root) { 60 | return { 61 | current: root, 62 | root: root, 63 | entering: true, 64 | next: next, 65 | resumeAt: resumeAt 66 | }; 67 | }; 68 | 69 | var Node = function(nodeType, sourcepos) { 70 | this._type = nodeType; 71 | this._parent = null; 72 | this._firstChild = null; 73 | this._lastChild = null; 74 | this._prev = null; 75 | this._next = null; 76 | this._sourcepos = sourcepos; 77 | this._open = true; 78 | this._string_content = null; 79 | this._literal = null; 80 | this._listData = {}; 81 | this._info = null; 82 | this._destination = null; 83 | this._title = null; 84 | this._isFenced = false; 85 | this._fenceChar = null; 86 | this._fenceLength = 0; 87 | this._fenceOffset = null; 88 | this._level = null; 89 | this._onEnter = null; 90 | this._onExit = null; 91 | }; 92 | 93 | var proto = Node.prototype; 94 | 95 | Object.defineProperty(proto, "isContainer", { 96 | get: function() { 97 | return isContainer(this); 98 | } 99 | }); 100 | 101 | Object.defineProperty(proto, "type", { 102 | get: function() { 103 | return this._type; 104 | } 105 | }); 106 | 107 | Object.defineProperty(proto, "firstChild", { 108 | get: function() { 109 | return this._firstChild; 110 | } 111 | }); 112 | 113 | Object.defineProperty(proto, "lastChild", { 114 | get: function() { 115 | return this._lastChild; 116 | } 117 | }); 118 | 119 | Object.defineProperty(proto, "next", { 120 | get: function() { 121 | return this._next; 122 | } 123 | }); 124 | 125 | Object.defineProperty(proto, "prev", { 126 | get: function() { 127 | return this._prev; 128 | } 129 | }); 130 | 131 | Object.defineProperty(proto, "parent", { 132 | get: function() { 133 | return this._parent; 134 | } 135 | }); 136 | 137 | Object.defineProperty(proto, "sourcepos", { 138 | get: function() { 139 | return this._sourcepos; 140 | } 141 | }); 142 | 143 | Object.defineProperty(proto, "literal", { 144 | get: function() { 145 | return this._literal; 146 | }, 147 | set: function(s) { 148 | this._literal = s; 149 | } 150 | }); 151 | 152 | Object.defineProperty(proto, "destination", { 153 | get: function() { 154 | return this._destination; 155 | }, 156 | set: function(s) { 157 | this._destination = s; 158 | } 159 | }); 160 | 161 | Object.defineProperty(proto, "title", { 162 | get: function() { 163 | return this._title; 164 | }, 165 | set: function(s) { 166 | this._title = s; 167 | } 168 | }); 169 | 170 | Object.defineProperty(proto, "info", { 171 | get: function() { 172 | return this._info; 173 | }, 174 | set: function(s) { 175 | this._info = s; 176 | } 177 | }); 178 | 179 | Object.defineProperty(proto, "level", { 180 | get: function() { 181 | return this._level; 182 | }, 183 | set: function(s) { 184 | this._level = s; 185 | } 186 | }); 187 | 188 | Object.defineProperty(proto, "listType", { 189 | get: function() { 190 | return this._listData.type; 191 | }, 192 | set: function(t) { 193 | this._listData.type = t; 194 | } 195 | }); 196 | 197 | Object.defineProperty(proto, "listTight", { 198 | get: function() { 199 | return this._listData.tight; 200 | }, 201 | set: function(t) { 202 | this._listData.tight = t; 203 | } 204 | }); 205 | 206 | Object.defineProperty(proto, "listStart", { 207 | get: function() { 208 | return this._listData.start; 209 | }, 210 | set: function(n) { 211 | this._listData.start = n; 212 | } 213 | }); 214 | 215 | Object.defineProperty(proto, "listDelimiter", { 216 | get: function() { 217 | return this._listData.delimiter; 218 | }, 219 | set: function(delim) { 220 | this._listData.delimiter = delim; 221 | } 222 | }); 223 | 224 | Object.defineProperty(proto, "onEnter", { 225 | get: function() { 226 | return this._onEnter; 227 | }, 228 | set: function(s) { 229 | this._onEnter = s; 230 | } 231 | }); 232 | 233 | Object.defineProperty(proto, "onExit", { 234 | get: function() { 235 | return this._onExit; 236 | }, 237 | set: function(s) { 238 | this._onExit = s; 239 | } 240 | }); 241 | 242 | Node.prototype.appendChild = function(child) { 243 | child.unlink(); 244 | child._parent = this; 245 | if (this._lastChild) { 246 | this._lastChild._next = child; 247 | child._prev = this._lastChild; 248 | this._lastChild = child; 249 | } else { 250 | this._firstChild = child; 251 | this._lastChild = child; 252 | } 253 | }; 254 | 255 | Node.prototype.prependChild = function(child) { 256 | child.unlink(); 257 | child._parent = this; 258 | if (this._firstChild) { 259 | this._firstChild._prev = child; 260 | child._next = this._firstChild; 261 | this._firstChild = child; 262 | } else { 263 | this._firstChild = child; 264 | this._lastChild = child; 265 | } 266 | }; 267 | 268 | Node.prototype.unlink = function() { 269 | if (this._prev) { 270 | this._prev._next = this._next; 271 | } else if (this._parent) { 272 | this._parent._firstChild = this._next; 273 | } 274 | if (this._next) { 275 | this._next._prev = this._prev; 276 | } else if (this._parent) { 277 | this._parent._lastChild = this._prev; 278 | } 279 | this._parent = null; 280 | this._next = null; 281 | this._prev = null; 282 | }; 283 | 284 | Node.prototype.insertAfter = function(sibling) { 285 | sibling.unlink(); 286 | sibling._next = this._next; 287 | if (sibling._next) { 288 | sibling._next._prev = sibling; 289 | } 290 | sibling._prev = this; 291 | this._next = sibling; 292 | sibling._parent = this._parent; 293 | if (!sibling._next) { 294 | sibling._parent._lastChild = sibling; 295 | } 296 | }; 297 | 298 | Node.prototype.insertBefore = function(sibling) { 299 | sibling.unlink(); 300 | sibling._prev = this._prev; 301 | if (sibling._prev) { 302 | sibling._prev._next = sibling; 303 | } 304 | sibling._next = this; 305 | this._prev = sibling; 306 | sibling._parent = this._parent; 307 | if (!sibling._prev) { 308 | sibling._parent._firstChild = sibling; 309 | } 310 | }; 311 | 312 | Node.prototype.walker = function() { 313 | var walker = new NodeWalker(this); 314 | return walker; 315 | }; 316 | 317 | export default Node; 318 | 319 | /* Example of use of walker: 320 | 321 | var walker = w.walker(); 322 | var event; 323 | 324 | while (event = walker.next()) { 325 | console.log(event.entering, event.node.type); 326 | } 327 | 328 | */ 329 | -------------------------------------------------------------------------------- /lib/render/html.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | import { escapeXml } from "../common.js"; 4 | import Renderer from "./renderer.js"; 5 | 6 | var reUnsafeProtocol = /^javascript:|vbscript:|file:|data:/i; 7 | var reSafeDataProtocol = /^data:image\/(?:png|gif|jpeg|webp)/i; 8 | 9 | var potentiallyUnsafe = function(url) { 10 | return reUnsafeProtocol.test(url) && !reSafeDataProtocol.test(url); 11 | }; 12 | 13 | // Helper function to produce an HTML tag. 14 | function tag(name, attrs, selfclosing) { 15 | if (this.disableTags > 0) { 16 | return; 17 | } 18 | this.buffer += "<" + name; 19 | if (attrs && attrs.length > 0) { 20 | var i = 0; 21 | var attrib; 22 | while ((attrib = attrs[i]) !== undefined) { 23 | this.buffer += " " + attrib[0] + '="' + attrib[1] + '"'; 24 | i++; 25 | } 26 | } 27 | if (selfclosing) { 28 | this.buffer += " /"; 29 | } 30 | this.buffer += ">"; 31 | this.lastOut = ">"; 32 | } 33 | 34 | function HtmlRenderer(options) { 35 | options = options || {}; 36 | // by default, soft breaks are rendered as newlines in HTML 37 | options.softbreak = options.softbreak || "\n"; 38 | // set to "
    " to make them hard breaks 39 | // set to " " if you want to ignore line wrapping in source 40 | this.esc = options.esc || escapeXml; 41 | // escape html with a custom function 42 | // else use escapeXml 43 | 44 | this.disableTags = 0; 45 | this.lastOut = "\n"; 46 | this.options = options; 47 | } 48 | 49 | /* Node methods */ 50 | 51 | function text(node) { 52 | this.out(node.literal); 53 | } 54 | 55 | function softbreak() { 56 | this.lit(this.options.softbreak); 57 | } 58 | 59 | function linebreak() { 60 | this.tag("br", [], true); 61 | this.cr(); 62 | } 63 | 64 | function link(node, entering) { 65 | var attrs = this.attrs(node); 66 | if (entering) { 67 | if (!(this.options.safe && potentiallyUnsafe(node.destination))) { 68 | attrs.push(["href", this.esc(node.destination)]); 69 | } 70 | if (node.title) { 71 | attrs.push(["title", this.esc(node.title)]); 72 | } 73 | this.tag("a", attrs); 74 | } else { 75 | this.tag("/a"); 76 | } 77 | } 78 | 79 | function image(node, entering) { 80 | if (entering) { 81 | if (this.disableTags === 0) { 82 | if (this.options.safe && potentiallyUnsafe(node.destination)) { 83 | this.lit('');
 84 |             } else {
 85 |                 this.lit('<img src='); 96 | } 97 | } 98 | } 99 | 100 | function emph(node, entering) { 101 | this.tag(entering ? "em" : "/em"); 102 | } 103 | 104 | function strong(node, entering) { 105 | this.tag(entering ? "strong" : "/strong"); 106 | } 107 | 108 | function paragraph(node, entering) { 109 | var grandparent = node.parent.parent, 110 | attrs = this.attrs(node); 111 | if (grandparent !== null && grandparent.type === "list") { 112 | if (grandparent.listTight) { 113 | return; 114 | } 115 | } 116 | if (entering) { 117 | this.cr(); 118 | this.tag("p", attrs); 119 | } else { 120 | this.tag("/p"); 121 | this.cr(); 122 | } 123 | } 124 | 125 | function heading(node, entering) { 126 | var tagname = "h" + node.level, 127 | attrs = this.attrs(node); 128 | if (entering) { 129 | this.cr(); 130 | this.tag(tagname, attrs); 131 | } else { 132 | this.tag("/" + tagname); 133 | this.cr(); 134 | } 135 | } 136 | 137 | function code(node) { 138 | this.tag("code"); 139 | this.out(node.literal); 140 | this.tag("/code"); 141 | } 142 | 143 | function code_block(node) { 144 | var info_words = node.info ? node.info.split(/\s+/) : [], 145 | attrs = this.attrs(node); 146 | if (info_words.length > 0 && info_words[0].length > 0) { 147 | var cls = this.esc(info_words[0]); 148 | if (!/^language-/.exec(cls)) { 149 | cls = "language-" + cls; 150 | } 151 | attrs.push(["class", cls]); 152 | } 153 | this.cr(); 154 | this.tag("pre"); 155 | this.tag("code", attrs); 156 | this.out(node.literal); 157 | this.tag("/code"); 158 | this.tag("/pre"); 159 | this.cr(); 160 | } 161 | 162 | function thematic_break(node) { 163 | var attrs = this.attrs(node); 164 | this.cr(); 165 | this.tag("hr", attrs, true); 166 | this.cr(); 167 | } 168 | 169 | function block_quote(node, entering) { 170 | var attrs = this.attrs(node); 171 | if (entering) { 172 | this.cr(); 173 | this.tag("blockquote", attrs); 174 | this.cr(); 175 | } else { 176 | this.cr(); 177 | this.tag("/blockquote"); 178 | this.cr(); 179 | } 180 | } 181 | 182 | function list(node, entering) { 183 | var tagname = node.listType === "bullet" ? "ul" : "ol", 184 | attrs = this.attrs(node); 185 | 186 | if (entering) { 187 | var start = node.listStart; 188 | if (start !== null && start !== 1) { 189 | attrs.push(["start", start.toString()]); 190 | } 191 | this.cr(); 192 | this.tag(tagname, attrs); 193 | this.cr(); 194 | } else { 195 | this.cr(); 196 | this.tag("/" + tagname); 197 | this.cr(); 198 | } 199 | } 200 | 201 | function item(node, entering) { 202 | var attrs = this.attrs(node); 203 | if (entering) { 204 | this.tag("li", attrs); 205 | } else { 206 | this.tag("/li"); 207 | this.cr(); 208 | } 209 | } 210 | 211 | function html_inline(node) { 212 | if (this.options.safe) { 213 | this.lit(""); 214 | } else { 215 | this.lit(node.literal); 216 | } 217 | } 218 | 219 | function html_block(node) { 220 | this.cr(); 221 | if (this.options.safe) { 222 | this.lit(""); 223 | } else { 224 | this.lit(node.literal); 225 | } 226 | this.cr(); 227 | } 228 | 229 | function custom_inline(node, entering) { 230 | if (entering && node.onEnter) { 231 | this.lit(node.onEnter); 232 | } else if (!entering && node.onExit) { 233 | this.lit(node.onExit); 234 | } 235 | } 236 | 237 | function custom_block(node, entering) { 238 | this.cr(); 239 | if (entering && node.onEnter) { 240 | this.lit(node.onEnter); 241 | } else if (!entering && node.onExit) { 242 | this.lit(node.onExit); 243 | } 244 | this.cr(); 245 | } 246 | 247 | /* Helper methods */ 248 | 249 | function out(s) { 250 | this.lit(this.esc(s)); 251 | } 252 | 253 | function attrs(node) { 254 | var att = []; 255 | if (this.options.sourcepos) { 256 | var pos = node.sourcepos; 257 | if (pos) { 258 | att.push([ 259 | "data-sourcepos", 260 | String(pos[0][0]) + 261 | ":" + 262 | String(pos[0][1]) + 263 | "-" + 264 | String(pos[1][0]) + 265 | ":" + 266 | String(pos[1][1]) 267 | ]); 268 | } 269 | } 270 | return att; 271 | } 272 | 273 | // quick browser-compatible inheritance 274 | HtmlRenderer.prototype = Object.create(Renderer.prototype); 275 | 276 | HtmlRenderer.prototype.text = text; 277 | HtmlRenderer.prototype.html_inline = html_inline; 278 | HtmlRenderer.prototype.html_block = html_block; 279 | HtmlRenderer.prototype.softbreak = softbreak; 280 | HtmlRenderer.prototype.linebreak = linebreak; 281 | HtmlRenderer.prototype.link = link; 282 | HtmlRenderer.prototype.image = image; 283 | HtmlRenderer.prototype.emph = emph; 284 | HtmlRenderer.prototype.strong = strong; 285 | HtmlRenderer.prototype.paragraph = paragraph; 286 | HtmlRenderer.prototype.heading = heading; 287 | HtmlRenderer.prototype.code = code; 288 | HtmlRenderer.prototype.code_block = code_block; 289 | HtmlRenderer.prototype.thematic_break = thematic_break; 290 | HtmlRenderer.prototype.block_quote = block_quote; 291 | HtmlRenderer.prototype.list = list; 292 | HtmlRenderer.prototype.item = item; 293 | HtmlRenderer.prototype.custom_inline = custom_inline; 294 | HtmlRenderer.prototype.custom_block = custom_block; 295 | 296 | HtmlRenderer.prototype.esc = escapeXml; 297 | 298 | HtmlRenderer.prototype.out = out; 299 | HtmlRenderer.prototype.tag = tag; 300 | HtmlRenderer.prototype.attrs = attrs; 301 | 302 | export default HtmlRenderer; 303 | -------------------------------------------------------------------------------- /test/test.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | "use strict"; 3 | 4 | // Using commonjs here so CI can run the test file on older Node.js versions 5 | var fs = require("fs"); 6 | var commonmark = require(".."); 7 | 8 | // Home made mini-version of the npm ansi module: 9 | var escSeq = function(s) { 10 | return function() { 11 | process.stdout.write("\u001b" + s); 12 | return this; 13 | }; 14 | }; 15 | 16 | var repeat = function(pattern, count) { 17 | if (count < 1) { 18 | return ""; 19 | } 20 | var result = ""; 21 | while (count > 1) { 22 | if (count & 1) { 23 | result += pattern; 24 | } 25 | count >>= 1; 26 | pattern += pattern; 27 | } 28 | return result + pattern; 29 | }; 30 | 31 | var cursor = { 32 | write: function(s) { 33 | process.stdout.write(s); 34 | return this; 35 | }, 36 | green: escSeq("[0;32m"), 37 | red: escSeq("[0;31m"), 38 | cyan: escSeq("[0;36m"), 39 | reset: escSeq("[0m") 40 | }; 41 | 42 | var writer = new commonmark.HtmlRenderer(); 43 | var reader = new commonmark.Parser(); 44 | var readerSmart = new commonmark.Parser({ smart: true }); 45 | 46 | var results = { 47 | passed: 0, 48 | failed: 0 49 | }; 50 | 51 | var showSpaces = function(s) { 52 | var t = s; 53 | return t.replace(/\t/g, "→").replace(/ /g, "␣"); 54 | }; 55 | 56 | var extractSpecTests = function(testfile) { 57 | var data = fs.readFileSync(testfile, "utf8"); 58 | var examples = []; 59 | var current_section = ""; 60 | var example_number = 0; 61 | var tests = data 62 | .replace(/\r\n?/g, "\n") // Normalize newlines for platform independence 63 | .replace(/^(.|[\n])*/m, ""); 64 | 65 | tests.replace( 66 | /^`{32} example\n([\s\S]*?)^\.\n([\s\S]*?)^`{32}$|^#{1,6} *(.*)$/gm, 67 | function(_, markdownSubmatch, htmlSubmatch, sectionSubmatch) { 68 | if (sectionSubmatch) { 69 | current_section = sectionSubmatch; 70 | } else { 71 | example_number++; 72 | examples.push({ 73 | markdown: markdownSubmatch, 74 | html: htmlSubmatch, 75 | section: current_section, 76 | number: example_number 77 | }); 78 | } 79 | } 80 | ); 81 | return examples; 82 | }; 83 | 84 | var specTest = function(testcase, res, converter) { 85 | var markdown = testcase.markdown.replace(/→/g, "\t"); 86 | var expected = testcase.html.replace(/→/g, "\t"); 87 | var actual = converter(markdown); 88 | if (actual === expected) { 89 | res.passed++; 90 | cursor 91 | .green() 92 | .write("✓") 93 | .reset(); 94 | } else { 95 | res.failed++; 96 | cursor.write("\n"); 97 | 98 | cursor.red().write("✘ Example " + testcase.number + "\n"); 99 | cursor.cyan(); 100 | cursor.write("=== markdown ===============\n"); 101 | cursor.write(showSpaces(markdown)); 102 | cursor.write("=== expected ===============\n"); 103 | cursor.write(showSpaces(expected)); 104 | cursor.write("=== got ====================\n"); 105 | cursor.write(showSpaces(actual)); 106 | cursor.reset(); 107 | } 108 | }; 109 | 110 | var specTests = function(testfile, res, converter) { 111 | cursor.write("Spec tests [" + testfile + "]:\n"); 112 | 113 | var current_section = ""; 114 | var examples = extractSpecTests(testfile); 115 | 116 | console.time("Elapsed time"); 117 | for (var i = 0; i < examples.length; i++) { 118 | var testcase = examples[i]; 119 | if (testcase.section !== current_section) { 120 | if (current_section !== "") { 121 | cursor.write("\n"); 122 | } 123 | current_section = testcase.section; 124 | cursor 125 | .reset() 126 | .write(current_section) 127 | .reset() 128 | .write(" "); 129 | } 130 | specTest(testcase, results, converter); 131 | } 132 | cursor.write("\n"); 133 | console.timeEnd("Elapsed time"); 134 | cursor.write("\n"); 135 | }; 136 | 137 | var pathologicalTest = function(testcase, res, converter) { 138 | cursor.write(testcase.name + " "); 139 | console.time(" elapsed time"); 140 | var actual = converter(testcase.input); 141 | if (actual === testcase.expected) { 142 | cursor 143 | .green() 144 | .write("✓\n") 145 | .reset(); 146 | res.passed += 1; 147 | } else { 148 | cursor.red().write("✘\n"); 149 | cursor.cyan(); 150 | cursor.write("=== markdown ===============\n"); 151 | cursor.write(showSpaces(testcase.input)); 152 | cursor.write("=== expected ===============\n"); 153 | cursor.write(showSpaces(testcase.expected)); 154 | cursor.write("=== got ====================\n"); 155 | cursor.write(showSpaces(actual)); 156 | cursor.write("\n"); 157 | cursor.reset(); 158 | res.failed += 1; 159 | } 160 | console.timeEnd(" elapsed time"); 161 | }; 162 | 163 | specTests("test/spec.txt", results, function(z) { 164 | return writer.render(reader.parse(z)); 165 | }); 166 | 167 | specTests("test/smart_punct.txt", results, function(z) { 168 | return writer.render(readerSmart.parse(z)); 169 | }); 170 | 171 | specTests("test/regression.txt", results, function(z) { 172 | return writer.render(reader.parse(z)); 173 | }); 174 | 175 | // pathological cases 176 | cursor.write("Pathological cases:\n"); 177 | 178 | var cases = [ 179 | { 180 | name: "U+0000 in input", 181 | input: "abc\u0000xyz\u0000\n", 182 | expected: "

    abc\ufffdxyz\ufffd

    \n" 183 | }, 184 | { 185 | name: "alternate line endings", 186 | input: "- a\n- b\r- c\r\n- d", 187 | expected: 188 | "
      \n
    • a
    • \n
    • b
    • \n
    • c
    • \n
    • d
    • \n
    \n" 189 | } 190 | ]; 191 | 192 | var x; 193 | for (x = 1000; x <= 10000; x *= 10) { 194 | cases.push({ 195 | name: "nested strong emph " + x + " deep", 196 | input: repeat("*a **a ", x) + "b" + repeat(" a** a*", x), 197 | expected: 198 | "

    " + 199 | repeat("a a ", x) + 200 | "b" + 201 | repeat(" a a", x) + 202 | "

    \n" 203 | }); 204 | } 205 | for (x = 1000; x <= 10000; x *= 10) { 206 | cases.push({ 207 | name: x + " emph closers with no openers", 208 | input: repeat("a_ ", x), 209 | expected: "

    " + repeat("a_ ", x - 1) + "a_

    \n" 210 | }); 211 | } 212 | for (x = 1000; x <= 10000; x *= 10) { 213 | cases.push({ 214 | name: x + " emph openers with no closers", 215 | input: repeat("_a ", x), 216 | expected: "

    " + repeat("_a ", x - 1) + "_a

    \n" 217 | }); 218 | } 219 | for (x = 1000; x <= 10000; x *= 10) { 220 | cases.push({ 221 | name: x + " openers and closers multiple of 3", 222 | input: "a**b" + repeat("c* ", x), 223 | expected: "

    a**b" + repeat("c* ", x - 1) + "c*

    \n" 224 | }); 225 | } 226 | for (x = 1000; x <= 10000; x *= 10) { 227 | cases.push({ 228 | name: x + " #172", 229 | input: repeat("*_* _ ", x), 230 | expected: "

    " + repeat("_ _ ", x - 1) + "_ _

    \n" 231 | }); 232 | } 233 | for (x = 1000; x <= 10000; x *= 10) { 234 | cases.push({ 235 | name: x + " link closers with no openers", 236 | input: repeat("a] ", x), 237 | expected: "

    " + repeat("a] ", x - 1) + "a]

    \n" 238 | }); 239 | } 240 | for (x = 1000; x <= 10000; x *= 10) { 241 | cases.push({ 242 | name: x + " link openers with no closers", 243 | input: repeat("[a ", x), 244 | expected: "

    " + repeat("[a ", x - 1) + "[a

    \n" 245 | }); 246 | } 247 | for (x = 1000; x <= 10000; x *= 10) { 248 | cases.push({ 249 | name: x + " link openers and emph closers", 250 | input: repeat("[ a_ ", x), 251 | expected: "

    " + repeat("[ a_ ", x - 1) + "[ a_

    \n" 252 | }); 253 | } 254 | for (x = 1000; x <= 10000; x *= 10) { 255 | cases.push({ 256 | name: x + " mismatched openers and closers", 257 | input: repeat("*a_ ", x), 258 | expected: "

    " + repeat("*a_ ", x - 1) + "*a_

    \n" 259 | }); 260 | } 261 | for (x = 1000; x <= 10000; x *= 10) { 262 | cases.push({ 263 | name: x + " pattern [ (](", 264 | input: repeat("[ (](", x), 265 | expected: "

    " + repeat("[ (](", x) + "

    \n" 266 | }); 267 | } 268 | for (x = 1000; x <= 10000; x *= 10) { 269 | cases.push({ 270 | name: "nested brackets " + x + " deep", 271 | input: repeat("[", x) + "a" + repeat("]", x), 272 | expected: "

    " + repeat("[", x) + "a" + repeat("]", x) + "

    \n" 273 | }); 274 | } 275 | for (x = 1000; x <= 10000; x *= 10) { 276 | cases.push({ 277 | name: "nested block quote " + x + " deep", 278 | input: repeat("> ", x) + "a\n", 279 | expected: 280 | repeat("
    \n", x) + 281 | "

    a

    \n" + 282 | repeat("
    \n", x) 283 | }); 284 | } 285 | for (x = 1000; x <= 10000; x *= 10) { 286 | cases.push({ 287 | name: "[\\\\... " + x + " deep", 288 | input: "[" + repeat("\\", x) + "\n", 289 | expected: "

    " + "[" + repeat("\\", x / 2) + "

    \n" 290 | }); 291 | } 292 | for (x = 10; x <= 1000; x *= 10) { 293 | cases.push({ 294 | name: x + " backslashes in unclosed link title", 295 | input: "[test](\\url \"" + repeat("\\", x) + "\n", 296 | expected: "

    [test](\\url "" + repeat("\\", x / 2) + "

    \n" 297 | }); 298 | } 299 | 300 | // Commented out til we have a fix... see #129 301 | // for (x = 1000; x <= 10000; x *= 10) { 302 | // cases.push( 303 | // { name: '[]( ' + x + ' deep', 304 | // input: repeat('[](', x) + '\n', 305 | // expected: '

    ' + repeat('[](', x) + '

    \n' 306 | // }); 307 | // } 308 | var parse_and_render = function(z) { 309 | return writer.render(reader.parse(z)); 310 | }; 311 | 312 | for (var j = 0; j < cases.length; j++) { 313 | pathologicalTest(cases[j], results, parse_and_render); 314 | } 315 | cursor.write("\n"); 316 | 317 | cursor.write( 318 | results.passed.toString() + 319 | " tests passed, " + 320 | results.failed.toString() + 321 | " failed.\n" 322 | ); 323 | 324 | process.exit(results.failed); 325 | -------------------------------------------------------------------------------- /test/regression.txt: -------------------------------------------------------------------------------- 1 | # Regression tests 2 | 3 | Eating a character after a partially consumed tab. 4 | 5 | ```````````````````````````````` example 6 | * foo 7 | →bar 8 | . 9 |
      10 |
    • foo 11 | bar
    • 12 |
    13 | ```````````````````````````````` 14 | 15 | Type 7 HTML block followed by whitespace (#98). 16 | 17 | ```````````````````````````````` example 18 | 19 | x 20 | . 21 | 22 | x 23 | ```````````````````````````````` 24 | 25 | h2..h6 raw HTML blocks (jgm/CommonMark#430). 26 | 27 | ```````````````````````````````` example 28 |

    lorem

    29 | 30 |

    lorem

    31 | 32 |

    lorem

    33 | 34 |

    lorem

    35 | 36 |
    lorem
    37 | 38 |
    lorem
    39 | . 40 |

    lorem

    41 |

    lorem

    42 |

    lorem

    43 |

    lorem

    44 |
    lorem
    45 |
    lorem
    46 | ```````````````````````````````` 47 | 48 | Issue #109 - tabs after setext header line 49 | 50 | 51 | ```````````````````````````````` example 52 | hi 53 | --→ 54 | . 55 |

    hi

    56 | ```````````````````````````````` 57 | 58 | Issue #108 - Chinese punctuation not recognized 59 | 60 | ```````````````````````````````` example 61 | **。**话 62 | . 63 |

    **。**话

    64 | ```````````````````````````````` 65 | 66 | Issue jgm/cmark#177 - incorrect emphasis parsing 67 | 68 | ```````````````````````````````` example 69 | a***b* c* 70 | . 71 |

    a*b c

    72 | ```````````````````````````````` 73 | 74 | Issue jgm/CommonMark#468 - backslash at end of link definition 75 | 76 | 77 | ```````````````````````````````` example 78 | [\]: test 79 | . 80 |

    []: test

    81 | ```````````````````````````````` 82 | 83 | Issue commonmark/commonmark.js#121 - punctuation set different 84 | 85 | ```````````````````````````````` example 86 | ^_test_ 87 | . 88 |

    ^test

    89 | ```````````````````````````````` 90 | 91 | Issue #116 - tabs before and after ATX closing heading 92 | ```````````````````````````````` example 93 | # foo→#→ 94 | . 95 |

    foo

    96 | ```````````````````````````````` 97 | 98 | commonmark/CommonMark#493 - escaped space not allowed in link destination. 99 | 100 | ```````````````````````````````` example 101 | [link](a\ b) 102 | . 103 |

    [link](a\ b)

    104 | ```````````````````````````````` 105 | 106 | Issue #527 - meta tags in inline contexts 107 | 108 | ```````````````````````````````` example 109 | City: 110 | 111 | 112 | 113 | . 114 |

    City: 115 | 116 | 117 |

    118 | ```````````````````````````````` 119 | 120 | Double-encoding. 121 | 122 | ```````````````````````````````` example 123 | [XSS](javascript&colon;alert%28'XSS'%29) 124 | . 125 |

    XSS

    126 | ```````````````````````````````` 127 | 128 | PR #179 129 | 130 | ```````````````````````````````` example 131 | [link](https://www.example.com/home/%25batty) 132 | . 133 |

    link

    134 | ```````````````````````````````` 135 | 136 | Issue commonamrk#517 - script, pre, style close tag without 137 | opener. 138 | 139 | ```````````````````````````````` example 140 | 141 | 142 | 143 | 144 | 145 | . 146 | 147 | 148 | 149 | ```````````````````````````````` 150 | 151 | Issue #289. 152 | 153 | ```````````````````````````````` example 154 | [a]( 155 | . 156 |

    [a](<b) c>

    157 | ```````````````````````````````` 158 | 159 | Issue #161. 160 | 161 | ```````````````````````````````` example 162 | *failed to be italic!*\ 163 | text 164 | . 165 |

    failed to be italic!
    166 | text

    167 | ```````````````````````````````` 168 | 169 | Issue #196. 170 | 171 | ```````````````````````````````` example 172 | a 174 | . 175 |

    a

    177 | ```````````````````````````````` 178 | 179 | Issue #211 180 | 181 | ```````````````````````````````` example 182 | [\ 183 | foo]: /uri 184 | 185 | [\ 186 | foo] 187 | . 188 |


    189 | foo

    190 | ```````````````````````````````` 191 | 192 | Issue #213 - type 7 blocks can't interrupt 193 | paragraph 194 | 195 | ```````````````````````````````` example 196 | - 200 | . 201 |
      202 |
    • 203 |
    • 208 |
    209 | ```````````````````````````````` 210 | 211 | Issue cmark/#383 - emphasis parsing. 212 | 213 | ```````````````````````````````` example 214 | *****Hello*world**** 215 | . 216 |

    **Helloworld

    217 | ```````````````````````````````` 218 | 219 | Issue reported at 220 | https://talk.commonmark.org/t/link-label-collapse-all-internal-whitespace/3919/5 221 | 222 | ```````````````````````````````` example 223 | [foo][one two 224 | three] 225 | 226 | [one two three]: /url "title" 227 | . 228 |

    foo

    229 | ```````````````````````````````` 230 | 231 | Issue #258 232 | 233 | ```````````````````````````````` example 234 | ``` 235 | abc 236 | ``` 237 | . 238 |
    abc
    239 | 
    240 | ```````````````````````````````` 241 | 242 | ` 245 | . 246 | 247 | ```````````````````````````````` 248 | 249 | Declarations don't need spaces, according to the spec (cmark#456) 250 | ```````````````````````````````` example 251 | x 252 | . 253 |

    x

    254 | ```````````````````````````````` 255 | 256 | Block-quoted blank line shouldn't make parent list loose. 257 | ```````````````````````````````` example 258 | ## Case 1 259 | 260 | - > a 261 | > 262 | - b 263 | 264 | 265 | ## Case 2 266 | 267 | - > - a 268 | > 269 | - b 270 | 271 | 272 | ## Case 3 273 | 274 | - > > a 275 | > 276 | - b 277 | 278 | 279 | ## Case 4 280 | 281 | - > # a 282 | > 283 | - b 284 | 285 | 286 | ## Case 5 287 | 288 | - ``` 289 | The following line is part of code block. 290 | 291 | - b 292 | 293 | ## Case 6 294 | 295 | - The following line is **not** part of code block. 296 | 297 | - b 298 | 299 | ## Case 7 300 | 301 | -
    The following line is part of HTML block.
    302 | 
    303 | - 
    304 | - b 305 | . 306 |

    Case 1

    307 |
      308 |
    • 309 |
      310 |

      a

      311 |
      312 |
    • 313 |
    • b
    • 314 |
    315 |

    Case 2

    316 |
      317 |
    • 318 |
      319 |
        320 |
      • a
      • 321 |
      322 |
      323 |
    • 324 |
    • b
    • 325 |
    326 |

    Case 3

    327 |
      328 |
    • 329 |
      330 |
      331 |

      a

      332 |
      333 |
      334 |
    • 335 |
    • b
    • 336 |
    337 |

    Case 4

    338 |
      339 |
    • 340 |
      341 |

      a

      342 |
      343 |
    • 344 |
    • b
    • 345 |
    346 |

    Case 5

    347 |
      348 |
    • 349 |
      The following line is part of code block.
      350 | 
      351 | 
      352 |
    • 353 |
    • b
    • 354 |
    355 |

    Case 6

    356 |
      357 |
    • 358 |
      The following line is **not** part of code block.
      359 | 
      360 |
    • 361 |
    • 362 |

      b

      363 |
    • 364 |
    365 |

    Case 7

    366 |
      367 |
    • 368 |
      The following line is part of HTML block.
      369 | 
      370 | 
    • 371 |
    • 372 | 373 |
    • 374 |
    • b
    • 375 |
    376 | ```````````````````````````````` 377 | 378 | Link reference definitions are blocks when checking list tightness. 379 | ```````````````````````````````` example 380 | ## Case 1 381 | 382 | - [aaa]: / 383 | 384 | [aaa]: / 385 | - b 386 | 387 | 388 | ## Case 2 389 | 390 | - a 391 | 392 | [aaa]: / 393 | - b 394 | 395 | 396 | ## Case 3 397 | 398 | - [aaa]: / 399 | 400 | a 401 | - b 402 | . 403 |

    Case 1

    404 |
      405 |
    • 406 |
    • 407 |

      b

      408 |
    • 409 |
    410 |

    Case 2

    411 |
      412 |
    • 413 |

      a

      414 |
    • 415 |
    • 416 |

      b

      417 |
    • 418 |
    419 |

    Case 3

    420 |
      421 |
    • 422 |

      a

      423 |
    • 424 |
    • 425 |

      b

      426 |
    • 427 |
    428 | ```````````````````````````````` 429 | 430 | An underscore that is not part of a delimiter should not prevent another 431 | pair of underscores from forming part of their own. 432 | ```````````````````````````````` example 433 | __!_!__ 434 | 435 | __!x!__ 436 | 437 | **!*!** 438 | 439 | --- 440 | 441 | _*__*_* 442 | 443 | _*xx*_* 444 | 445 | _*__-_- 446 | 447 | _*xx-_- 448 | . 449 |

    !_!

    450 |

    !x!

    451 |

    !*!

    452 |
    453 |

    __*

    454 |

    xx*

    455 |

    *__--

    456 |

    *xx--

    457 | ```````````````````````````````` 458 | 459 | #277: 460 | ```````````````````````````````` example 461 | ```language-r 462 | x <- 1 463 | ``` 464 | 465 | ```r 466 | x <- 1 467 | ``` 468 | . 469 |
    x <- 1
    470 | 
    471 |
    x <- 1
    472 | 
    473 | ```````````````````````````````` 474 | 475 | #278 476 | ```````````````````````````````` example 477 | ¶g; 478 | 479 | ¶ 480 | 481 | ¶ 482 | . 483 |

    &parag;

    484 |

    &para

    485 |

    486 | ```````````````````````````````` 487 | 488 | #281 489 | ```````````````````````````````` example 490 | [test]:example 491 | ""third [test] 492 | . 493 |

    ""third test

    494 | ```````````````````````````````` 495 | 496 | #283 497 | ```````````````````````````````` example 498 | x 499 | 500 | x 501 | . 502 |

    x

    503 |

    x<!>

    504 | ```````````````````````````````` 505 | 506 | #285 507 | ```````````````````````````````` example 508 | foo 509 | 510 | foo 511 | 512 | foo 513 | 514 | foo more --> 515 | . 516 |

    foo

    517 |

    foo

    518 |

    foo

    519 |

    foo more -->

    520 | ```````````````````````````````` 521 | 522 | #261 523 | ```````````````````````````````` example 524 | Vertical Tab 525 | 526 | Form Feed 527 | 528 |  NBSP (U+00A0) NBSP  529 | 530 |  Em Space (U+2003) Em Space  531 | 532 | 
Line Separator (U+2028) Line Separator
 533 | 534 | 
Paragraph Separator (U+2029) Paragraph Separator
 535 | 536 |  全角スペース (U+3000) 全形空白  537 | 538 | ZWNBSP (U+FEFF) ZWNBSP 539 | . 540 |

    Vertical Tab

    541 |

    Form Feed

    542 |

     NBSP (U+00A0) NBSP 

    543 |

     Em Space (U+2003) Em Space 

    544 |

    
Line Separator (U+2028) Line Separator


    545 |

    
Paragraph Separator (U+2029) Paragraph Separator


    546 |

     全角スペース (U+3000) 全形空白 

    547 |

    ZWNBSP (U+FEFF) ZWNBSP

    548 | ```````````````````````````````` 549 | 550 | #296 551 | ```````````````````````````````` example 552 | a**a∇**a 553 | 554 | a**∇a**a 555 | 556 | a**a𝜵**a 557 | 558 | a**𝜵a**a 559 | . 560 |

    a**a∇**a

    561 |

    a**∇a**a

    562 |

    a**a𝜵**a

    563 |

    a**𝜵a**a

    564 | ```````````````````````````````` 565 | -------------------------------------------------------------------------------- /bench/samples/README.md: -------------------------------------------------------------------------------- 1 | CommonMark 2 | ========== 3 | 4 | CommonMark is a rationalized version of Markdown syntax, 5 | with a [spec][the spec] and BSD3-licensed reference 6 | implementations in C and JavaScript. 7 | 8 | [Try it now!](http://spec.commonmark.org/dingus.html) 9 | 10 | The implementations 11 | ------------------- 12 | 13 | The C implementation provides both a shared library (`libcmark`) and a 14 | standalone program `cmark` that converts CommonMark to HTML. It is 15 | written in standard C99 and has no library dependencies. The parser is 16 | very fast (see [benchmarks](benchmarks.md)). 17 | 18 | It is easy to use `libcmark` in python, lua, ruby, and other dynamic 19 | languages: see the `wrappers/` subdirectory for some simple examples. 20 | 21 | The JavaScript implementation provides both an NPM package and a 22 | single JavaScript file, with no dependencies, that can be linked into 23 | an HTML page. For further information, see the 24 | [README in the js directory](js/README.md). 25 | 26 | **A note on security:** 27 | Neither implementation attempts to sanitize link attributes or 28 | raw HTML. If you use these libraries in applications that accept 29 | untrusted user input, you must run the output through an HTML 30 | sanitizer to protect against 31 | [XSS attacks](http://en.wikipedia.org/wiki/Cross-site_scripting). 32 | 33 | Installing (C) 34 | -------------- 35 | 36 | Building the C program (`cmark`) and shared library (`libcmark`) 37 | requires [cmake]. If you modify `scanners.re`, then you will also 38 | need [re2c], which is used to generate `scanners.c` from 39 | `scanners.re`. We have included a pre-generated `scanners.c` in 40 | the repository to reduce build dependencies. 41 | 42 | If you have GNU make, you can simply `make`, `make test`, and `make 43 | install`. This calls [cmake] to create a `Makefile` in the `build` 44 | directory, then uses that `Makefile` to create the executable and 45 | library. The binaries can be found in `build/src`. 46 | 47 | For a more portable method, you can use [cmake] manually. [cmake] knows 48 | how to create build environments for many build systems. For example, 49 | on FreeBSD: 50 | 51 | mkdir build 52 | cd build 53 | cmake .. # optionally: -DCMAKE_INSTALL_PREFIX=path 54 | make # executable will be created as build/src/cmark 55 | make test 56 | make install 57 | 58 | Or, to create Xcode project files on OSX: 59 | 60 | mkdir build 61 | cd build 62 | cmake -G Xcode .. 63 | make 64 | make test 65 | make install 66 | 67 | The GNU Makefile also provides a few other targets for developers. 68 | To run a benchmark: 69 | 70 | make bench 71 | 72 | To run a "fuzz test" against ten long randomly generated inputs: 73 | 74 | make fuzztest 75 | 76 | To run a test for memory leaks using `valgrind`: 77 | 78 | make leakcheck 79 | 80 | To reformat source code using `astyle`: 81 | 82 | make astyle 83 | 84 | To make a release tarball and zip archive: 85 | 86 | make archive 87 | 88 | 89 | Compiling for Windows 90 | --------------------- 91 | 92 | To compile with MSVC and NMAKE: 93 | 94 | nmake 95 | 96 | You can cross-compile a Windows binary and dll on linux if you have the 97 | `mingw32` compiler: 98 | 99 | make mingw 100 | 101 | The binaries will be in `build-mingw/windows/bin`. 102 | 103 | Installing (JavaScript) 104 | ----------------------- 105 | 106 | The JavaScript library can be installed through `npm`: 107 | 108 | npm install commonmark 109 | 110 | This includes a command-line converter called `commonmark`. 111 | 112 | If you want to use it in a client application, you can fetch 113 | a pre-built copy of `commonmark.js` from 114 | . 115 | 116 | For further information, see the 117 | [README in the js directory](js/README.md). 118 | 119 | The spec 120 | -------- 121 | 122 | [The spec] contains over 500 embedded examples which serve as conformance 123 | tests. To run the tests using an executable `$PROG`: 124 | 125 | python3 test/spec_tests.py --program $PROG 126 | 127 | If you want to extract the raw test data from the spec without 128 | actually running the tests, you can do: 129 | 130 | python3 test/spec_tests.py --dump-tests 131 | 132 | and you'll get all the tests in JSON format. 133 | 134 | [The spec]: http://spec.commonmark.org/0.13/ 135 | 136 | The source of [the spec] is `spec.txt`. This is basically a Markdown 137 | file, with code examples written in a shorthand form: 138 | 139 | . 140 | Markdown source 141 | . 142 | expected HTML output 143 | . 144 | 145 | To build an HTML version of the spec, do `make spec.html`. To build a 146 | PDF version, do `make spec.pdf`. (Creating a PDF requires [pandoc] 147 | and a LaTeX installation. Creating the HTML version requires only 148 | `libcmark` and `python3`.) 149 | 150 | The spec is written from the point of view of the human writer, not 151 | the computer reader. It is not an algorithm---an English translation of 152 | a computer program---but a declarative description of what counts as a block 153 | quote, a code block, and each of the other structural elements that can 154 | make up a Markdown document. 155 | 156 | Because John Gruber's [canonical syntax 157 | description](http://daringfireball.net/projects/markdown/syntax) leaves 158 | many aspects of the syntax undetermined, writing a precise spec requires 159 | making a large number of decisions, many of them somewhat arbitrary. 160 | In making them, we have appealed to existing conventions and 161 | considerations of simplicity, readability, expressive power, and 162 | consistency. We have tried to ensure that "normal" documents in the many 163 | incompatible existing implementations of Markdown will render, as far as 164 | possible, as their authors intended. And we have tried to make the rules 165 | for different elements work together harmoniously. In places where 166 | different decisions could have been made (for example, the rules 167 | governing list indentation), we have explained the rationale for 168 | my choices. In a few cases, we have departed slightly from the canonical 169 | syntax description, in ways that we think further the goals of Markdown 170 | as stated in that description. 171 | 172 | For the most part, we have limited ourselves to the basic elements 173 | described in Gruber's canonical syntax description, eschewing extensions 174 | like footnotes and definition lists. It is important to get the core 175 | right before considering such things. However, we have included a visible 176 | syntax for line breaks and fenced code blocks. 177 | 178 | Differences from original Markdown 179 | ---------------------------------- 180 | 181 | There are only a few places where this spec says things that contradict 182 | the canonical syntax description: 183 | 184 | - It allows all punctuation symbols to be backslash-escaped, 185 | not just the symbols with special meanings in Markdown. We found 186 | that it was just too hard to remember which symbols could be 187 | escaped. 188 | 189 | - It introduces an alternative syntax for hard line 190 | breaks, a backslash at the end of the line, supplementing the 191 | two-spaces-at-the-end-of-line rule. This is motivated by persistent 192 | complaints about the “invisible” nature of the two-space rule. 193 | 194 | - Link syntax has been made a bit more predictable (in a 195 | backwards-compatible way). For example, `Markdown.pl` allows single 196 | quotes around a title in inline links, but not in reference links. 197 | This kind of difference is really hard for users to remember, so the 198 | spec allows single quotes in both contexts. 199 | 200 | - The rule for HTML blocks differs, though in most real cases it 201 | shouldn't make a difference. (See the section on HTML Blocks 202 | for details.) The spec's proposal makes it easy to include Markdown 203 | inside HTML block-level tags, if you want to, but also allows you to 204 | exclude this. It is also makes parsing much easier, avoiding 205 | expensive backtracking. 206 | 207 | - It does not collapse adjacent bird-track blocks into a single 208 | blockquote: 209 | 210 | > this is two 211 | 212 | > blockquotes 213 | 214 | > this is a single 215 | > 216 | > blockquote with two paragraphs 217 | 218 | - Rules for content in lists differ in a few respects, though (as with 219 | HTML blocks), most lists in existing documents should render as 220 | intended. There is some discussion of the choice points and 221 | differences in the subsection of List Items entitled Motivation. 222 | We think that the spec's proposal does better than any existing 223 | implementation in rendering lists the way a human writer or reader 224 | would intuitively understand them. (We could give numerous examples 225 | of perfectly natural looking lists that nearly every existing 226 | implementation flubs up.) 227 | 228 | - The spec stipulates that two blank lines break out of all list 229 | contexts. This is an attempt to deal with issues that often come up 230 | when someone wants to have two adjacent lists, or a list followed by 231 | an indented code block. 232 | 233 | - Changing bullet characters, or changing from bullets to numbers or 234 | vice versa, starts a new list. We think that is almost always going 235 | to be the writer's intent. 236 | 237 | - The number that begins an ordered list item may be followed by 238 | either `.` or `)`. Changing the delimiter style starts a new 239 | list. 240 | 241 | - The start number of an ordered list is significant. 242 | 243 | - Fenced code blocks are supported, delimited by either 244 | backticks (```` ``` ```` or tildes (` ~~~ `). 245 | 246 | Contributing 247 | ------------ 248 | 249 | There is a [forum for discussing 250 | CommonMark](http://talk.commonmark.org); you should use it instead of 251 | github issues for questions and possibly open-ended discussions. 252 | Use the [github issue tracker](http://github.com/jgm/CommonMark/issues) 253 | only for simple, clear, actionable issues. 254 | 255 | Authors 256 | ------- 257 | 258 | The spec was written by John MacFarlane, drawing on 259 | 260 | - his experience writing and maintaining Markdown implementations in several 261 | languages, including the first Markdown parser not based on regular 262 | expression substitutions ([pandoc](http://github.com/jgm/pandoc)) and 263 | the first markdown parsers based on PEG grammars 264 | ([peg-markdown](http://github.com/jgm/peg-markdown), 265 | [lunamark](http://github.com/jgm/lunamark)) 266 | - a detailed examination of the differences between existing Markdown 267 | implementations using [BabelMark 2](http://johnmacfarlane.net/babelmark2/), 268 | and 269 | - extensive discussions with David Greenspan, Jeff Atwood, Vicent 270 | Marti, Neil Williams, and Benjamin Dumke-von der Ehe. 271 | 272 | John MacFarlane was also responsible for the original versions of the 273 | C and JavaScript implementations. The block parsing algorithm was 274 | worked out together with David Greenspan. Vicent Marti 275 | optimized the C implementation for performance, increasing its speed 276 | tenfold. Kārlis Gaņģis helped work out a better parsing algorithm 277 | for links and emphasis, eliminating several worst-case performance 278 | issues. Nick Wellnhofer contributed many improvements, including 279 | most of the C library's API and its test harness. Vitaly Puzrin 280 | has offered much good advice about the JavaScript implementation. 281 | 282 | [cmake]: http://www.cmake.org/download/ 283 | [pandoc]: http://johnmacfarlane.net/pandoc/ 284 | [re2c]: http://re2c.org 285 | 286 | -------------------------------------------------------------------------------- /bench/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 10 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |

    Benchmarks

    22 |
    23 |
    24 | 117 | 118 | 119 | 120 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | commonmark.js 2 | ============= 3 | 4 | [![Build Status](https://github.com/commonmark/commonmark.js/workflows/CI%20tests/badge.svg)](https://github.com/commonmark/commonmark.js/actions) 5 | [![NPM version](https://img.shields.io/npm/v/commonmark.svg?style=flat)](https://www.npmjs.org/package/commonmark) 6 | 7 | 8 | CommonMark is a rationalized version of Markdown syntax, 9 | with a [spec][the spec] and BSD-licensed reference 10 | implementations in C and JavaScript. 11 | 12 | [the spec]: http://spec.commonmark.org 13 | 14 | For more information, see . 15 | 16 | This repository contains the JavaScript reference implementation. 17 | It provides a library with functions for parsing CommonMark 18 | documents to an abstract syntax tree (AST), manipulating the AST, 19 | and rendering the document to HTML or to an XML representation of the 20 | AST. 21 | 22 | To play with this library without installing it, see 23 | the live dingus at . 24 | 25 | Installing 26 | ---------- 27 | 28 | You can install the library using `npm`: 29 | 30 | npm install commonmark 31 | 32 | This package includes the commonmark library and a 33 | command-line executable, `commonmark`. 34 | 35 | For client-side use, you can use one of the single-file 36 | distributions provided in the `dist/` subdirectory 37 | of the node installation (`node_modules/commonmark/dist/`). 38 | Use either `commonmark.js` (readable source) or 39 | `commonmark.min.js` (minimized source). 40 | 41 | Alternatively, `bower install commonmark` will install 42 | the needed distribution files in 43 | `bower_components/commonmark/dist`. 44 | 45 | You can also use the version hosted by unpkg: for example, 46 | 47 | for the unminimized version 0.29.3. 48 | 49 | 50 | Building 51 | -------- 52 | 53 | Make sure to fetch dependencies with: 54 | 55 | npm install 56 | 57 | To run tests for the JavaScript library: 58 | 59 | npm test 60 | 61 | (Running the tests will also rebuild distribution files in 62 | `dist/`.) 63 | 64 | To run benchmarks against some other JavaScript converters: 65 | 66 | make bench 67 | 68 | To start an interactive dingus that you can use to try out 69 | the library: 70 | 71 | make dingus 72 | 73 | Usage 74 | ----- 75 | 76 | Instead of converting Markdown directly to HTML, as most converters 77 | do, `commonmark.js` parses Markdown to an AST (abstract syntax tree), 78 | and then renders this AST as HTML. This opens up the possibility of 79 | manipulating the AST between parsing and rendering. For example, one 80 | could transform emphasis into ALL CAPS. 81 | 82 | Here's a basic usage example: 83 | 84 | ``` js 85 | var reader = new commonmark.Parser(); 86 | var writer = new commonmark.HtmlRenderer(); 87 | var parsed = reader.parse("Hello *world*"); // parsed is a 'Node' tree 88 | // transform parsed if you like... 89 | var result = writer.render(parsed); // result is a String 90 | ``` 91 | 92 | The constructors for `Parser` and `HtmlRenderer` take an optional 93 | `options` parameter: 94 | 95 | ``` js 96 | var reader = new commonmark.Parser({smart: true}); 97 | var writer = new commonmark.HtmlRenderer({sourcepos: true}); 98 | ``` 99 | 100 | `Parser` currently supports the following: 101 | 102 | - `smart`: if `true`, straight quotes will be made curly, `--` will 103 | be changed to an en dash, `---` will be changed to an em dash, and 104 | `...` will be changed to ellipses. 105 | 106 | Both `HtmlRenderer` and `XmlRenderer` (see below) support these options: 107 | 108 | - `sourcepos`: if `true`, source position information for block-level 109 | elements will be rendered in the `data-sourcepos` attribute (for 110 | HTML) or the `sourcepos` attribute (for XML). 111 | - `safe`: if `true`, raw HTML will not be passed through to HTML 112 | output (it will be replaced by comments), and potentially unsafe 113 | URLs in links and images (those beginning with `javascript:`, 114 | `vbscript:`, `file:`, and with a few exceptions `data:`) will 115 | be replaced with empty strings. 116 | - `softbreak`: specify raw string to be used for a softbreak. 117 | - `esc`: specify a function to be used to escape strings. Its 118 | argument is the string. 119 | 120 | For example, to make soft breaks render as hard breaks in HTML: 121 | 122 | ``` js 123 | var writer = new commonmark.HtmlRenderer({softbreak: "
    "}); 124 | ``` 125 | 126 | To make them render as spaces: 127 | 128 | ``` js 129 | var writer = new commonmark.HtmlRenderer({softbreak: " "}); 130 | ``` 131 | 132 | `XmlRenderer` serves as an alternative to `HtmlRenderer` and 133 | will produce an XML representation of the AST: 134 | 135 | ``` js 136 | var writer = new commonmark.XmlRenderer({sourcepos: true}); 137 | ``` 138 | 139 | The parser returns a Node. The following public properties are defined 140 | (those marked "read-only" have only a getter, not a setter): 141 | 142 | - `type` (read-only): a String, one of 143 | `text`, `softbreak`, `linebreak`, `emph`, `strong`, 144 | `html_inline`, `link`, `image`, `code`, `document`, `paragraph`, 145 | `block_quote`, `item`, `list`, `heading`, `code_block`, 146 | `html_block`, `thematic_break`. 147 | - `firstChild` (read-only): a Node or null. 148 | - `lastChild` (read-only): a Node or null. 149 | - `next` (read-only): a Node or null. 150 | - `prev` (read-only): a Node or null. 151 | - `parent` (read-only): a Node or null. 152 | - `sourcepos` (read-only): an Array with the following form: 153 | `[[startline, startcolumn], [endline, endcolumn]]`. 154 | - `isContainer` (read-only): `true` if the Node can contain other 155 | Nodes as children. 156 | - `literal`: the literal String content of the node or null. 157 | - `destination`: link or image destination (String) or null. 158 | - `title`: link or image title (String) or null. 159 | - `info`: fenced code block info string (String) or null. 160 | - `level`: heading level (Number). 161 | - `listType`: a String, either `bullet` or `ordered`. 162 | - `listTight`: `true` if list is tight. 163 | - `listStart`: a Number, the starting number of an ordered list. 164 | - `listDelimiter`: a String, either `)` or `.` for an ordered list. 165 | - `onEnter`, `onExit`: Strings, used only for `custom_block` or 166 | `custom_inline`. 167 | 168 | Nodes have the following public methods: 169 | 170 | - `appendChild(child)`: Append a Node `child` to the end of the 171 | Node's children. 172 | - `prependChild(child)`: Prepend a Node `child` to the 173 | beginning of the Node's children. 174 | - `unlink()`: Remove the Node from the tree, severing its links 175 | with siblings and parents, and closing up gaps as needed. 176 | - `insertAfter(sibling)`: Insert a Node `sibling` after the Node. 177 | - `insertBefore(sibling)`: Insert a Node `sibling` before the Node. 178 | - `walker()`: Returns a NodeWalker that can be used to iterate through 179 | the Node tree rooted in the Node. 180 | 181 | The NodeWalker returned by `walker()` has two methods: 182 | 183 | - `next()`: Returns an object with properties `entering` (a boolean, 184 | which is `true` when we enter a Node from a parent or sibling, and 185 | `false` when we reenter it from a child). Returns `null` when 186 | we have finished walking the tree. 187 | - `resumeAt(node, entering)`: Resets the iterator to resume at the 188 | specified node and setting for `entering`. (Normally this isn't 189 | needed unless you do destructive updates to the Node tree.) 190 | 191 | Here is an example of the use of a NodeWalker to iterate through 192 | the tree, making transformations. This simple example converts 193 | the contents of all `text` nodes to ALL CAPS: 194 | 195 | ``` js 196 | var walker = parsed.walker(); 197 | var event, node; 198 | 199 | while ((event = walker.next())) { 200 | node = event.node; 201 | if (event.entering && node.type === 'text') { 202 | node.literal = node.literal.toUpperCase(); 203 | } 204 | } 205 | ``` 206 | 207 | This more complex example converts emphasis to ALL CAPS: 208 | 209 | ``` js 210 | var walker = parsed.walker(); 211 | var event, node; 212 | var inEmph = false; 213 | 214 | while ((event = walker.next())) { 215 | node = event.node; 216 | if (node.type === 'emph') { 217 | if (event.entering) { 218 | inEmph = true; 219 | } else { 220 | inEmph = false; 221 | // add Emph node's children as siblings 222 | while (node.firstChild) { 223 | node.insertBefore(node.firstChild); 224 | } 225 | // remove the empty Emph node 226 | node.unlink() 227 | } 228 | } else if (inEmph && node.type === 'text') { 229 | node.literal = node.literal.toUpperCase(); 230 | } 231 | } 232 | ``` 233 | 234 | Exercises for the reader: write a transform to 235 | 236 | 1. De-linkify a document, transforming links to regular text. 237 | 2. Remove all raw HTML (`html_inline` and `html_block` nodes). 238 | 3. Run fenced code blocks marked with a language name through 239 | a syntax highlighting library, replacing them with an `HtmlBlock` 240 | containing the highlighted code. 241 | 4. Print warnings to the console for images without image 242 | descriptions or titles. 243 | 244 | Command line 245 | ------------ 246 | 247 | The command line executable parses CommonMark input from the 248 | specified files, or from stdin if no files are specified, and 249 | renders the result to stdout as HTML. If multiple input files 250 | are specified, their contents are concatenated before parsing, 251 | with newlines between them. 252 | 253 | ``` 254 | commonmark inputfile.md > outputfile.html 255 | commonmark intro.md chapter1.md chapter2.md > book.html 256 | ``` 257 | 258 | Use `commonmark --help` to get a summary of options. 259 | 260 | A note on security 261 | ------------------ 262 | 263 | The library does not attempt to sanitize link attributes or 264 | raw HTML. If you use this library in applications that accept 265 | untrusted user input, you should either enable the `safe` option 266 | (see above) or run the output through an HTML sanitizer to protect against 267 | [XSS attacks](http://en.wikipedia.org/wiki/Cross-site_scripting). 268 | 269 | Performance 270 | ----------- 271 | 272 | Performance is excellent, roughly on par with `marked`. On a benchmark 273 | converting an 11 MB Markdown file built by concatenating the Markdown 274 | sources of all localizations of the first edition of 275 | [*Pro Git*](https://github.com/progit/progit/tree/master/en) by Scott 276 | Chacon, the command-line tool, `commonmark` is just a bit slower than 277 | the C program `discount`, roughly ten times faster than PHP Markdown, 278 | a hundred times faster than Python Markdown, and more than 279 | a thousand times faster than `Markdown.pl`. 280 | 281 | Here are some focused benchmarks of four JavaScript libraries 282 | (using versions available on 24 Jan 2015). They test performance 283 | on different kinds of Markdown texts. (Most of these samples 284 | are taken from the 285 | [markdown-it](https://github.com/markdown-it/markdown-it) 286 | repository.) Results show a ratio of ops/second (higher is better) 287 | against showdown (which is usually the slowest implementation). 288 | Versions: showdown 1.3.0, marked 0.3.5, commonmark.js 0.22.1, 289 | markdown-it 5.0.2, node 5.3.0. Hardware: 1.6GHz Intel Core i5, Mac OSX. 290 | 291 | | Sample |showdown |commonmark|marked |markdown-it| 292 | |--------------------------|---------:|---------:|---------:|----------:| 293 | |[README.md] | 1| 3.6| 3.1| 3.9| 294 | |[block-bq-flat.md] | 1| 4.8| 4.9| 4.9| 295 | |[block-bq-nested.md] | 1| 11.9| 6.8| 10.7| 296 | |[block-code.md] | 1| 4.7| 12.1| 23.0| 297 | |[block-fences.md] | 1| 6.2| 21.2| 19.1| 298 | |[block-heading.md] | 1| 5.0| 4.8| 6.5| 299 | |[block-hr.md] | 1| 3.5| 3.3| 3.5| 300 | |[block-html.md] | 1| 2.1| 0.9| 3.8| 301 | |[block-lheading.md] | 1| 5.1| 4.9| 3.9| 302 | |[block-list-flat.md] | 1| 4.7| 4.4| 7.4| 303 | |[block-list-nested.md] | 1| 9.5| 7.8| 17.6| 304 | |[block-ref-flat.md] | 1| 0.8| 0.5| 0.6| 305 | |[block-ref-nested.md] | 1| 0.7| 0.6| 0.9| 306 | |[inline-autolink.md] | 1| 2.3| 3.4| 2.5| 307 | |[inline-backticks.md] | 1| 7.6| 5.3| 8.2| 308 | |[inline-em-flat.md] | 1| 1.5| 1.1| 1.6| 309 | |[inline-em-nested.md] | 1| 1.8| 1.3| 1.7| 310 | |[inline-em-worst.md] | 1| 2.4| 1.5| 2.5| 311 | |[inline-entity.md] | 1| 2.0| 3.8| 2.7| 312 | |[inline-escape.md] | 1| 2.2| 1.4| 5.0| 313 | |[inline-html.md] | 1| 2.9| 3.7| 3.3| 314 | |[inline-links-flat.md] | 1| 2.7| 2.7| 2.2| 315 | |[inline-links-nested.md] | 1| 1.4| 0.5| 0.5| 316 | |[inline-newlines.md] | 1| 2.3| 2.0| 3.5| 317 | |[lorem1.md] | 1| 6.0| 2.9| 3.3| 318 | |[rawtabs.md] | 1| 4.6| 3.9| 6.7| 319 | 320 | [block-html.md]: bench/samples/block-html.md 321 | [inline-links-nested.md]: bench/samples/inline-links-nested.md 322 | [inline-em-flat.md]: bench/samples/inline-em-flat.md 323 | [inline-autolink.md]: bench/samples/inline-autolink.md 324 | [inline-html.md]: bench/samples/inline-html.md 325 | [rawtabs.md]: bench/samples/rawtabs.md 326 | [inline-escape.md]: bench/samples/inline-escape.md 327 | [inline-em-worst.md]: bench/samples/inline-em-worst.md 328 | [block-list-nested.md]: bench/samples/block-list-nested.md 329 | [block-bq-nested.md]: bench/samples/block-bq-nested.md 330 | [block-bq-flat.md]: bench/samples/block-bq-flat.md 331 | [inline-newlines.md]: bench/samples/inline-newlines.md 332 | [block-ref-nested.md]: bench/samples/block-ref-nested.md 333 | [block-fences.md]: bench/samples/block-fences.md 334 | [lorem1.md]: bench/samples/lorem1.md 335 | [README.md]: bench/samples/README.md 336 | [inline-links-flat.md]: bench/samples/inline-links-flat.md 337 | [block-heading.md]: bench/samples/block-heading.md 338 | [inline-em-nested.md]: bench/samples/inline-em-nested.md 339 | [inline-entity.md]: bench/samples/inline-entity.md 340 | [block-list-flat.md]: bench/samples/block-list-flat.md 341 | [block-hr.md]: bench/samples/block-hr.md 342 | [block-lheading.md]: bench/samples/block-lheading.md 343 | [block-code.md]: bench/samples/block-code.md 344 | [inline-backticks.md]: bench/samples/inline-backticks.md 345 | [block-ref-flat.md]: bench/samples/block-ref-flat.md 346 | 347 | To generate this table: 348 | 349 | make bench-detailed 350 | 351 | Authors 352 | ------- 353 | 354 | John MacFarlane wrote the first version of the JavaScript 355 | implementation. The block parsing algorithm was worked out together 356 | with David Greenspan. Kārlis Gaņģis helped work out a better parsing 357 | algorithm for links and emphasis, eliminating several worst-case 358 | performance issues. Vitaly Puzrin has offered much good advice 359 | about optimization and other issues. 360 | -------------------------------------------------------------------------------- /changelog.txt: -------------------------------------------------------------------------------- 1 | 0.31.2] 2 | 3 | * Require minimist >= 1.2.8 (#290), see CVE-2021-44906. 4 | 5 | [0.31.1] 6 | 7 | * Fix HTML comment parsing with `-` before closing `-->` 8 | (#285, Robin Stocker). 9 | * Accept lowercase inline HTML declarations (Michael Howell). 10 | * Fix title-related backtracking with empty string (#281, 11 | Michael Howell). 12 | * Remove `string.prototype.repeat` polyfill (Steven). 13 | * Remove `source`, add `search` to list of recognized block tags. 14 | (a spec 0.31 change we forgot in last release). 15 | 16 | [0.31.0] 17 | 18 | * Update to 0.31 spec.txt. 19 | * Treat unicode symbols like punctuation for purposes of flankingness. 20 | This updates the library to conform to the 0.31 spec. 21 | * Do not process `&`-entities that don't end in `;` (#278, Michael Howell). 22 | * Html renderer: don't add `language-` to code block class 23 | if the info string already starts with `language-` (#277). 24 | * Fix pathological regex for HTML comments (#273). 25 | * Track underscore bottom separately mod 3, like asterisk (Michael Howell). 26 | * Fix list tightness (taku0). 27 | * Fix "CommomMark" typo (#270, Martin Geisler). 28 | * Declarations do not need a space, per the spec (commonmark/cmark#456). 29 | * Allow `` is no longer incorrectly accepted as an angle-bracketed 186 | link destination. 187 | * package.json: require lodash >= 4.17.11. 188 | * Require cached-path-relative >= 1.0.2. 189 | This fixes a security vulnerability, but it's only 190 | in the dev dependencies. 191 | * Update fenced block parsing for spec change. 192 | * Require space before title in reference link. 193 | See commonmark/cmark#263. 194 | * Update code span normalization for spec change. 195 | * Removed meta from list of block tags. See commonmark/CommonMark#527. 196 | * make dist: ensure that comment line is included in dist files (#144). 197 | Also change URL to CommonMark/commonmark.js. 198 | * Use local development dependencies (#142, Lynn Kirby). 199 | Packages used during development are now listed in devDependencies of 200 | package.json. Makefiles are updated to use those local versions. 201 | References to manually installing packages are removed from README.md 202 | and bench/bench.js. The package-lock.json file used in newer NPM 203 | versions is also added. 204 | * Allow spaces in pointy-bracket link destinations. 205 | * Adjust max length for decimal/numeric entities. 206 | See commonmark/CommonMark#487. 207 | * Don't allow escaped spaces in link destination. 208 | Closes commonmark/CommonMark#493. 209 | * Don't allow list items that are indented >= 4 spaces. 210 | See commonmark/CommonMark#497. 211 | 212 | [0.28.1] 213 | 214 | * Update changelog (omitted from 0.28.0 release). 215 | 216 | [0.28.0] 217 | 218 | * Update spec to 0.28. 219 | * Align punctuation regex with spec (#121). Previously some ASCII 220 | punctuation characters were not being counted, so `^_test_` came out 221 | without emphasis, for example. 222 | * Simplified a logical test, making it closer to the wording of the spec. 223 | * Don't parse reference def if last `]` is escaped (#468). 224 | E.g. 225 | 226 | [\ ] 227 | 228 | [\]: test 229 | * Dingus Makefile: remove ref to obsolete html.js. 230 | * Removed obsolete lib/xml.js (replaced by lib/render/xml.js). 231 | * Allow tabs before and after ATX closing header (Erik Edrosa). 232 | * Change precedence of Strong/Emph when both nestings possible. 233 | This accommodates the spec change to rule 14. 234 | Note that commonmark.js was not previously in conformity 235 | with rule 14 for things like `***hi****`. 236 | * Calculate "mulitple of 3" for delim runs based on original number 237 | of delims, not the number remaining after some have been 238 | used. 239 | * Make esc() method abstract and overridable (muji). 240 | * README: update documentation for overriding softbreak and esc (#118). 241 | * Remove old XMLRenderer implementation (muji). 242 | * package.json: use shorter form for repository. 243 | * Don't export version in lib/index.js. 244 | Instead, users can get version from package.json: 245 | `require('commonmark/package.json').version`. 246 | * Removed remnants of old html renderer (#113). 247 | Now we use lib/renderer/html.js. 248 | * Hand-rolled parser for link destinations. 249 | This allows nested parens, as now required by the spec. 250 | * Fix regression test example (Colin O'Dell). 251 | * dingus: Fixed iframe on load. 252 | 253 | [0.27.0] 254 | 255 | * Update spec to 0.27. 256 | * Use correct name in DOCTYPE declaration for XML output. 257 | It should be document, not CommonMark. 258 | * Fix Node type names in README (Jan Marthedal Rasmussen). 259 | * Allow shortcut link before a `(`. See jgm/CommonMark#427. 260 | * Added all characters in Pc, Pd, Pe, Pf, Pi, Po, Ps to rePunctuation 261 | (#108, problem not recognizing East Asian punctuation). 262 | * Allow tab after setext header line (#109). 263 | * Recognize h2..h6 as block tags (see jgm/CommonMark#430). 264 | * Enforce spec's distinction between Unicode whitespace and regular whitespace 265 | (Timothy Gu, see jgm/CommonMark#343). Per ECMA-262 6th Edition 266 | ("ECMAScript 2015") §21.2.2.12 [CharacterClassEscape], the JavaScript `\s` 267 | escape character matches the characters specified by "Unicode whitespace," 268 | but not "whitespace." Rename the existing regular expression variable to 269 | `UnicodeWhitespace`, and create and use a new regular expression variable 270 | that only matches the limited set of "whitespace" characters. 271 | * Removed unused definition. 272 | * Update README.md on overriding softbreak and escaping in 273 | renderer (#118). 274 | 275 | [0.26.0] 276 | 277 | * Implemented spec changes to lists: 278 | - Don't allow ordered lists to interrupt a paragraph unless 279 | they start with 1. 280 | - Remove two-blanks-break-out-of-lists feature. 281 | - Blank list item can't interrupt paragraph. 282 | * Fixed minor regex bug with raw HTML blocks (#98). 283 | This would affect things like: 284 | 285 | [SPACE][SPACE] 286 | x 287 | 288 | which, with the change, gets parsed as a raw HTML block, instead of a 289 | single paragraph with inline HTML, a line break, and 'x'. The new 290 | behavior conforms to the spec. See #7 in 4.6. Added regression. 291 | * Remove unnecessary check (Nik Ryby). It looks like `columns` is always 292 | true in this block, so there's no need to check it during the assignment 293 | to `count`. 294 | * Simplify and optimize brackets processing (links/images) (Robin Stocker). 295 | Together, these changes make the "nested brackets 10000 deep" 296 | pathological case go from 400 ms to 20 ms. 297 | * Changes in emph/strong emph parsing to match changes in spec. 298 | This implements the rule that we can't have emphasis matches 299 | when (a) one of the delimiters can open AND can close, and (b) 300 | the sum of the lengths of the delimiter runs containing open 301 | and close delimiters is a multiple of 3. 302 | * Fix not existing property usage (Maksim Dzikun). 303 | * Fixed tabs in ATX headers and thematic breaks. 304 | * Remove unused write-only variable (Maksim Dzikun). 305 | 306 | [0.25.1] 307 | 308 | * Ensure that `advanceNextNonspace` resets `partiallyConsumedTab`. 309 | This fixes a regression in which the first character after a tab 310 | would sometimes be dropped. 311 | * Added regression tests. 312 | * XML renderer: escape attribute values (muji). 313 | * Fix dingus vulnerability (muji). Use an iframe and innerHTML to prevent 314 | `