├── .gitignore ├── .travis.yml ├── HISTORY.md ├── LICENSE.md ├── README.md ├── docs └── assets │ └── custom.scss ├── index.js ├── package.json └── test ├── index.js └── support └── test.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | _docpress 3 | /coverage 4 | yarn.lock 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - '4' 4 | cache: 5 | directories: 6 | - node_modules 7 | script: 8 | - npm run coverage 9 | - npm run lint 10 | -------------------------------------------------------------------------------- /HISTORY.md: -------------------------------------------------------------------------------- 1 | ## [v1.2.2] 2 | > Jan 2, 2017 3 | 4 | No functional changes. 5 | 6 | - Update documentation and devDependencies 7 | - Reduce npm download size 8 | 9 | ## [v1.2.1] 10 | > Jan 3, 2016 11 | 12 | - Fix compatibility with older Node.js versions. 13 | 14 | ## [v1.2.0] 15 | > Nov 12, 2015 16 | 17 | - Don't throw errors in tags that can't be decorated. 18 | 19 | ## [v1.1.0] 20 | > Nov 11, 2015 21 | 22 | - Allow styling of `` in code blocks. 23 | 24 | ## [v1.0.0] 25 | > Oct 15, 2015 26 | 27 | - Bump major version - no functional changes. 28 | 29 | ## [v0.3.1] 30 | > Oct 14, 2015 31 | 32 | - Fix HTML comments not being omitted properly. 33 | 34 | ## [v0.3.0] 35 | > Oct 14, 2015 36 | 37 | - Large refactors to make it more stable. 38 | 39 | ## [v0.2.0] 40 | > Oct 14, 2015 41 | 42 | - Add support for inline elements. 43 | 44 | ## v0.1.0 45 | > Oct 14, 2015 46 | 47 | - Initial release. 48 | 49 | [v0.2.0]: https://github.com/rstacruz/markdown-it-decorate/compare/v0.1.0...v0.2.0 50 | [v0.3.0]: https://github.com/rstacruz/markdown-it-decorate/compare/v0.2.0...v0.3.0 51 | [v0.3.1]: https://github.com/rstacruz/markdown-it-decorate/compare/v0.3.0...v0.3.1 52 | [v1.0.0]: https://github.com/rstacruz/markdown-it-decorate/compare/v0.3.1...v1.0.0 53 | [v1.1.0]: https://github.com/rstacruz/markdown-it-decorate/compare/v1.0.0...v1.1.0 54 | [v1.2.0]: https://github.com/rstacruz/markdown-it-decorate/compare/v1.1.0...v1.2.0 55 | [v1.2.1]: https://github.com/rstacruz/markdown-it-decorate/compare/v1.2.0...v1.2.1 56 | [v1.2.2]: https://github.com/rstacruz/markdown-it-decorate/compare/v1.2.1...v1.2.2 -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015-2017 Rico Sta. Cruz. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # markdown-it-decorate 2 | 3 | > Add attributes, IDs and classes to Markdown 4 | 5 | Annotate your Markdown documents with HTML comments to add classes to HTML elements. Supported for and tested on [markdown-it] 6.x, 7.x, and 8.x. 6 | 7 | ```html 8 | Hello, from *Markdown*! 9 | 10 | ``` 11 | 12 | ```html 13 |

Hello, from Markdown!

14 | ``` 15 | 16 | [![Status](https://travis-ci.org/rstacruz/markdown-it-decorate.svg?branch=master)](https://travis-ci.org/rstacruz/markdown-it-decorate "See test builds") 17 | 18 | ## Usage 19 | 20 | Install the `markdown-it-decorate` package alongside `markdown-it` (they are peer dependencies). 21 | 22 | ```sh 23 | yarn add --exact markdown-it markdown-it-decorate 24 | # or: 25 | npm install --save --save-exact markdown-it markdown-it-decorate 26 | ``` 27 | 28 | `markdown-it-decorate` can be loaded as a plugin using `use()`. 29 | 30 | ```js 31 | const md = require('markdown-it')() 32 | .use(require('markdown-it-decorate')) 33 | ``` 34 | 35 | ## Cheatsheet 36 | 37 | You can add classes, ID's, or attributes. (See [§ Annotating elements](#annotating-elements)) 38 | 39 | | Source | Output | 40 | |----|----| 41 | | `Text ` | `

Text

` | 42 | | `Text ` | `

Text

` | 43 | | `# Hola ` | `

Hola

` | 44 | | `# Hola ` | `

Hola

` | 45 | | `# Hola ` | `

Hola

` | 46 | | `![Image](img.jpg)` | `Image` | 47 | 48 | You can specify the element name to decorate. (See [§ Disambiguating](#disambiguating)) 49 | 50 | | Source | Output | 51 | |----|----| 52 | | `> > Hi *world* ` | `
Hi world...` | 53 | | `> > Hi *world* ` | `
Hi world...` | 54 | | `> > Hi *world* ` | `
Hi world...` | 55 | 56 | ## Annotating elements 57 | 58 | Create an HTML comment in the format ``, where `...` can be a `.class`, `#id`, `key=attr` or a combination of any of them. The spaces around `{}` are optional. Be sure to render markdownIt with `html: true` to enable parsing of ``. 59 | 60 | ```html 61 | 62 | 63 | 64 | 65 | 66 | 67 | ``` 68 | 69 | You can put the comment in the same line or in the next. I recommend keeping it on a separate line, since this will make GitHub ignore it. 70 | 71 | ```html 72 | # Hello! 73 | ``` 74 | 75 | ```html 76 | # Hello! 77 | 78 | ``` 79 | 80 | ## Disambiguating 81 | 82 | By default, annotations will be applied to the deepest element preceding it. In the case below, `.wide` will be applied to the link (*"Next"*). 83 | 84 | ```md 85 | > This is a blockquote 86 | > 87 | > * It has a list. 88 | > * You can specify tag names. [Next](#next) 89 | > 90 | ``` 91 | 92 | ### Specifying elements 93 | 94 | To make it apply to a different element, precede your annotations with the tag name followed by a `:`. 95 | 96 | ```md 97 | > * It has a list. 98 | > * You can specify tag names. [Next](#next) 99 | ``` 100 | 101 | ### Combining 102 | 103 | You can combine them as you need. In this example, the link gets `.button`, the list item gets `.wide`, and the blockquote gets `.bordered`. 104 | 105 | ```md 106 | > * [Continue](#continue) 107 | 108 | 109 | 110 | ``` 111 | 112 | ```html 113 |
114 | 119 |
120 | ``` 121 | 122 | ### Selecting same names 123 | 124 | To go back to previous parent with the same name, add `^n` after the tag name, where `n` is how many levels deep to go back to. Using `^0` is the same as not specifying it at all. (This convention is taken from [gitrevisions](http://git-scm.com/docs/gitrevisions).) 125 | 126 | ```md 127 | > > > targets 3rd quote 128 | ``` 129 | 130 | ```md 131 | > > > targets 2nd quote 132 | ``` 133 | 134 | ```md 135 | > > > targets 1st quote 136 | ``` 137 | 138 | ### Decorating code blocks 139 | 140 | You can decorate code blocks. You may choose to decorate `pre`, `code`, or even both. 141 | 142 | ``` 143 | return true; 144 | ``` 145 | 146 | 147 | Indented code blocks are only supported in markdown-it 7.x or later. 148 | 149 | ``` 150 | // this is a code block 151 | return true; 152 | 153 | 154 | ``` 155 | 156 | ## Prior art 157 | 158 | * This is initially based off of [arve0/markdown-it-attrs](https://github.com/arve0/markdown-it-attrs) which uses text to annotate blocks (eg, `{.class #id}`). markdown-it-attr's approach was based off of [Pandoc's header attributes](http://pandoc.org/README.html#extension-header_attributes). 159 | 160 | * [Maruku](http://maruku.rubyforge.org/) (Ruby Markdown parser) also allows for block-level attributes and classnames with its [meta-data syntax](http://maruku.rubyforge.org/proposal.html). The syntax is similar to PanDoc's syntax (`{: .class #id}`). 161 | 162 | * [Kramdown](http://kramdown.gettalong.org/) (Ruby markdown parser) also supports the same syntax, also with a colon (`{: .class #id}`). 163 | 164 | ## Motivation 165 | 166 | markdown-it-decorate is inspired by the design of those features and improves on them: 167 | 168 | * Elements are marked via HTML comments; they'll be invisible to other Markdown parsers like GitHub's. 169 | * It supports inline elements in addition to block elements. 170 | 171 | ## Thanks 172 | 173 | **markdown-it-decorate** © 2015-2017, Rico Sta. Cruz. Released under the [MIT] License.
174 | Authored and maintained by Rico Sta. Cruz with help from contributors ([list][contributors]). 175 | 176 | > [ricostacruz.com](http://ricostacruz.com)  ·  177 | > GitHub [@rstacruz](https://github.com/rstacruz)  ·  178 | > Twitter [@rstacruz](https://twitter.com/rstacruz) 179 | 180 | [MIT]: http://mit-license.org/ 181 | [contributors]: http://github.com/rstacruz/markdown-it-decorate/contributors 182 | [markdown-it]: https://www.npmjs.com/package/markdown-it 183 | -------------------------------------------------------------------------------- /docs/assets/custom.scss: -------------------------------------------------------------------------------- 1 | @import url('http://ricostacruz.com/docpress-rsc/style.css'); 2 | 3 | html .page-index .markdown-body h1 { 4 | font-size: 3em; 5 | padding-bottom: 0; 6 | margin-bottom: .1em; 7 | } 8 | 9 | html .page-index .markdown-body h1 + p { 10 | font-size: 1.7em; 11 | color: #777; 12 | font-weight: 300; 13 | } 14 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | /* eslint-disable no-cond-assign */ 3 | 4 | var tagExpr = /^\n?$/ 5 | 6 | module.exports = function attributes (md) { 7 | md.core.ruler.push('curly_attributes', curlyAttrs) 8 | } 9 | 10 | /* 11 | * List of tag -> token type mappings. Eg, `
  • ` is `list_item_open`. 12 | */ 13 | 14 | var opening = { 15 | li: ['list_item'], 16 | ul: ['bullet_list'], 17 | p: ['paragraph'], 18 | ol: ['ordered_list'], 19 | blockquote: ['blockquote'], 20 | h1: ['heading'], 21 | h2: ['heading'], 22 | h3: ['heading'], 23 | h4: ['heading'], 24 | h5: ['heading'], 25 | h6: ['heading'], 26 | a: ['link'], 27 | code: ['code_inline', 'code_block', 'fence'] 28 | } 29 | 30 | var selfClosing = { 31 | hr: true, 32 | image: true 33 | } 34 | 35 | /** 36 | * ... 37 | */ 38 | 39 | function curlyAttrs (state) { 40 | var tokens = state.tokens 41 | var omissions = [] 42 | var parent, m 43 | var stack = { len: 0, contents: [], types: {} } 44 | 45 | tokens.forEach(function (token, i) { 46 | // Save breadcrumbs so html_block will pick it up 47 | if (isOpener(token.type) || selfClosing[token.type]) { 48 | spush(stack, token) 49 | } 50 | 51 | // "# Hello\n" 52 | // ...sequence of [heading_open, inline, heading_close, html_block] 53 | if (token.type === 'html_block') { 54 | m = token.content.match(tagExpr) 55 | if (!m) return 56 | 57 | parent = findParent(stack, m[1], m[2]) 58 | if (parent && applyToToken(parent, m[3])) { 59 | omissions.unshift(i) 60 | } 61 | } 62 | 63 | // "# Hello " 64 | // { type: 'inline', children: { ..., '' } } 65 | if (token.type === 'inline') { 66 | curlyInline(token.children, stack) 67 | } 68 | }) 69 | 70 | // Remove html_block tokens 71 | omissions.forEach(function (idx) { return tokens.splice(idx, 1) }) 72 | } 73 | 74 | /** 75 | * Internal: checks in a token type is a block opener 76 | */ 77 | 78 | function isOpener (type) { 79 | return type.match(/_(open|start)$/) || 80 | type === 'fence' || type === 'code_block' 81 | } 82 | 83 | /** 84 | * Internal: Run through inline and stuff 85 | */ 86 | 87 | function curlyInline (children, stack) { 88 | var lastText, m, parent 89 | 90 | // Keep a list of sub-tokens to be removed 91 | var omissions = [] 92 | 93 | children.forEach(function (child, i) { 94 | if (isOpener(child.type) || 95 | selfClosing[child.type] || 96 | child.type === 'code_inline') { 97 | spush(stack, child) 98 | } 99 | 100 | // Decorate tags are found 101 | if (m = child.content.match(tagExpr)) { 102 | var tag = m[1] 103 | var depth = m[2] 104 | var attrs = m[3] 105 | 106 | // Remove the comment, then remove the extra space 107 | parent = findParent(stack, tag, depth) 108 | if (parent && applyToToken(parent, attrs)) { 109 | omissions.unshift(i) 110 | if (lastText) trimRight(lastText, 'content') 111 | } 112 | } 113 | 114 | if (child.type === 'text') lastText = child 115 | }) 116 | 117 | // Remove them in a separate step so we don't 118 | omissions.forEach(function (idx) { 119 | children.splice(idx, 1) 120 | }) 121 | } 122 | 123 | /** 124 | * Private: given a list of tokens `list` and `lastParent`, find the one that 125 | * matches `tag`. 126 | */ 127 | 128 | function findParent (stack, tag, depth) { 129 | if (!tag) return stack.last 130 | 131 | if (depth === '^') { 132 | depth = 1 133 | } else if (typeof depth === 'string') { /* '^2' */ 134 | depth = +depth.substr(1) 135 | } else { 136 | depth = 0 137 | } 138 | 139 | var targets = opening[tag.toLowerCase()] || [tag.toLowerCase()] 140 | 141 | var target = targets.filter(function (target) { 142 | return stack.types[target] 143 | }) 144 | 145 | var list = stack.types[target] 146 | if (!list) return // Can't find tag `tag` 147 | 148 | return list[list.length - 1 - depth] 149 | } 150 | 151 | /** 152 | * Private: trim the right 153 | */ 154 | 155 | function trimRight (obj, attr) { 156 | obj[attr] = obj[attr].replace(/\s*$/, '') 157 | } 158 | 159 | /** 160 | * Private: apply tag to token 161 | * 162 | * applyToToken(token, '.classname') 163 | */ 164 | 165 | function applyToToken (token, attrs) { 166 | var m 167 | var todo = [] 168 | 169 | while (attrs.length > 0) { 170 | if (m = attrs.match(/^\s*\.([a-zA-Z0-9\-_]+)/)) { 171 | todo.push([ 'class', m[1], { append: true } ]) 172 | shift() 173 | } else if (m = attrs.match(/^\s*#([a-zA-Z0-9\-_]+)/)) { 174 | todo.push([ 'id', m[1] ]) 175 | shift() 176 | } else if (m = attrs.match(/^\s*([a-zA-Z0-9\-_]+)="([^"]*)"/)) { 177 | todo.push([ m[1], m[2] ]) 178 | shift() 179 | } else if (m = attrs.match(/^\s*([a-zA-Z0-9\-_]+)='([^']*)'/)) { 180 | todo.push([ m[1], m[2] ]) 181 | shift() 182 | } else if (m = attrs.match(/^\s*([a-zA-Z0-9\-_]+)=([^ ]*)/)) { 183 | todo.push([ m[1], m[2] ]) 184 | shift() 185 | } else if (m = attrs.match(/^\s*([a-zA-Z0-9\-_]+)/)) { 186 | todo.push([ m[1], '' ]) 187 | shift() 188 | } else if (m = attrs.match(/^\s+/)) { 189 | shift() 190 | } else { 191 | return 192 | } 193 | } 194 | 195 | todo.forEach(function (args) { setAttr.apply(this, [token].concat(args)) }) 196 | return true 197 | 198 | function shift () { 199 | attrs = attrs.substr(m[0].length) 200 | } 201 | } 202 | 203 | /** 204 | * Private: sets an attribute `attr` to `value` in a token. If `options.append` 205 | * is true, append to the old value instead of overwriting it. 206 | */ 207 | 208 | function setAttr (token, attr, value, options) { 209 | var idx = token.attrIndex(attr) 210 | 211 | if (idx === -1) { 212 | token.attrPush([ attr, value ]) 213 | } else if (options && options.append) { 214 | token.attrs[idx][1] = 215 | token.attrs[idx][1] + ' ' + value 216 | } else { 217 | token.attrs[idx][1] = value 218 | } 219 | } 220 | 221 | /** 222 | * Private: pushes a token to the stack 223 | */ 224 | 225 | function spush (stack, token) { 226 | var type = token.type.replace(/_(open|start)$/, '') 227 | if (!stack.types[type]) { stack.types[type] = [] } 228 | stack.types[type].push(token) 229 | stack.last = token 230 | } 231 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "markdown-it-decorate", 3 | "description": "Add classes, identifiers and attributes to your markdown with HTML comments", 4 | "version": "1.2.2", 5 | "author": "Rico Sta. Cruz ", 6 | "bugs": { 7 | "url": "https://github.com/rstacruz/markdown-it-decorate/issues" 8 | }, 9 | "dependencies": {}, 10 | "devDependencies": { 11 | "istanbul": "0.4.5", 12 | "markdown-it": "8.2.2", 13 | "standard": "8.6.0", 14 | "tap-spec": "4.1.1", 15 | "tape": "4.6.3" 16 | }, 17 | "directories": { 18 | "test": "test" 19 | }, 20 | "homepage": "https://github.com/rstacruz/markdown-it-decorate#readme", 21 | "files": [ 22 | ], 23 | "keywords": [ 24 | "attribute", 25 | "class", 26 | "curly brackets", 27 | "header_attributes", 28 | "id", 29 | "identifier", 30 | "markdown-it", 31 | "markdown-it-plugin", 32 | "pandoc" 33 | ], 34 | "license": "MIT", 35 | "main": "index.js", 36 | "repository": { 37 | "type": "git", 38 | "url": "git+https://github.com/rstacruz/markdown-it-decorate.git" 39 | }, 40 | "scripts": { 41 | "test": "node test/index.js | tap-spec", 42 | "lint": "standard", 43 | "coverage": "istanbul cover test/index.js" 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var markdownIt = require('markdown-it') 4 | var decorate = require('../') 5 | var run = require('./support/test') 6 | var test = require('tape') 7 | 8 | run = run.bind(this, markdownIt({ html: true }).use(decorate)) 9 | 10 | test('classes:', function (t) { 11 | run(t, 'text ', '

    text

    \n') 12 | run(t, 'text ', '

    text

    \n') 13 | run(t, 'text ', '

    text

    \n') 14 | run(t, 'text ', '

    text

    \n') 15 | run(t, 'text ', '

    text

    \n') 16 | run(t, 'text ', '

    text

    \n') 17 | t.end() 18 | }) 19 | 20 | test('spaces:', function (t) { 21 | run(t, 'text ', '

    text

    \n') 22 | run(t, 'text ', '

    text

    \n') 23 | run(t, 'text ', '

    text

    \n') 24 | run(t, 'text ', '

    text

    \n') 25 | run(t, 'text ', '

    text

    \n') 26 | run(t, 'text ', '

    text

    \n') 27 | run(t, 'text', '

    text

    \n') 28 | run(t, 'text', '

    text

    \n') 29 | run(t, 'text', '

    text

    \n') 30 | run(t, 'text', '

    text

    \n') 31 | run(t, 'text', '

    text

    \n') 32 | run(t, 'text', '

    text

    \n') 33 | t.end() 34 | }) 35 | 36 | test('ids:', function (t) { 37 | run(t, 'text ', '

    text

    \n') 38 | run(t, 'text ', '

    text

    \n') 39 | run(t, 'text ', '

    text

    \n') 40 | t.end() 41 | }) 42 | 43 | test('new lines:', function (t) { 44 | run(t, 'text\n', '

    text

    \n') 45 | run(t, 'text\n\n', '

    text

    \n') 46 | run(t, 'text\n\n\nhi', '

    text

    \n

    hi

    \n') 47 | run(t, 'text\n\nhi', '

    text

    \n

    hi

    \n') 48 | run(t, 'text\n\n\nhi', '

    text

    \n

    hi

    \n') 49 | t.end() 50 | }) 51 | 52 | test('no attribute value:', function (t) { 53 | run(t, 'text ', '

    text

    \n') 54 | run(t, 'text ', '

    text

    \n') 55 | run(t, 'text ', '

    text

    \n') 56 | run(t, 'text ', '

    text

    \n') 57 | t.end() 58 | }) 59 | 60 | test('falses:', function (t) { 61 | run(t, 'text ', '

    text

    \n') 62 | run(t, 'text\n', '

    text

    \n') 63 | run(t, '', '') 64 | t.end() 65 | }) 66 | 67 | test('in the middle:', function (t) { 68 | run(t, 'text foo', '

    text foo

    \n') 69 | t.end() 70 | }) 71 | 72 | test('multiple tags', function (t) { 73 | run(t, 'text foo', '

    text foo

    \n') 74 | run(t, 'text foo', '

    text foo

    \n') 75 | run(t, '*hi*', '

    hi

    \n') 76 | t.end() 77 | }) 78 | 79 | test('combinations', function (t) { 80 | run(t, 'text ', '

    text

    \n') 81 | run(t, 'text ', '

    text

    \n') 82 | run(t, 'text ', '

    text

    \n') 83 | run(t, 'text [link](/) ', '

    text link

    \n') 84 | run(t, 'text\n', '

    text

    \n') 85 | run(t, 'text ', '

    text

    \n') 86 | t.end() 87 | }) 88 | 89 | test('attributes', function (t) { 90 | run(t, 'text ', '

    text

    \n') 91 | run(t, 'text ', '

    text

    \n') 92 | run(t, 'text ', '

    text

    \n') 93 | run(t, 'text ', '

    text

    \n') 94 | run(t, 'text ', '

    text

    \n') 95 | run(t, 'text ', '

    text

    \n') 96 | t.end() 97 | }) 98 | 99 | test('h1 (atx)', function (t) { 100 | run(t, '# h1 ', '

    h1

    \n') 101 | t.end() 102 | }) 103 | 104 | test('h1 with formatting', function (t) { 105 | run(t, '# *h1* ', '

    h1

    \n') 106 | t.end() 107 | }) 108 | 109 | test('nested inline formatting:', function (t) { 110 | run(t, '# ***yo***', '

    yo

    \n') 111 | run(t, '# ***yo***', '

    yo

    \n') 112 | run(t, '# ***yo***\n\n', '

    yo

    \n') 113 | t.end() 114 | }) 115 | 116 | test('h1 (lined):', function (t) { 117 | run(t, 'h1\n==\n', '

    h1

    \n') 118 | t.end() 119 | }) 120 | 121 | test('blockquote:', function (t) { 122 | run(t, '> text ', '
    \n

    text

    \n
    \n') 123 | run(t, '> text\n', '
    \n

    text

    \n
    \n') 124 | run(t, '> text\n> text\n', '
    \n

    text\ntext

    \n
    \n') 125 | t.end() 126 | }) 127 | 128 | test('lists:', function (t) { 129 | run(t, '* text\n', '
      \n
    • text
    • \n
    \n') 130 | run(t, '* * text\n', '
      \n
    • \n
        \n
      • text
      • \n
      \n
    • \n
    \n') 131 | t.end() 132 | }) 133 | 134 | test('image:', function (t) { 135 | run(t, '![](hi.jpg)', '

    \n') 136 | t.end() 137 | }) 138 | 139 | test('horizontal rule:', function (t) { 140 | run(t, '----\n', '
    \n') 141 | t.end() 142 | }) 143 | 144 | test('horizontal rule, nested:', function (t) { 145 | run(t, '> ----\n', '
    \n
    \n
    \n') 146 | t.end() 147 | }) 148 | 149 | test('tables:', function (t) { 150 | run(t, '| x | y |\n|---|---|\n| a | b |\n', { match: /^/ }) 151 | t.end() 152 | }) 153 | 154 | test.skip('tables (pending):', function (t) { 155 | run(t, '| x | y |\n|---|---|\n| a | b |\n', { match: /^/ }) 156 | run(t, '| x | y |\n|---|---|\n| a | b |\n', { match: /^/ }) 158 | t.end() 159 | }) 160 | 161 | test('specifying tags:', function (t) { 162 | run(t, '* text ', '
      \n
    • text
    • \n
    \n') 163 | run(t, '* text ', '
      \n
    • text
    • \n
    \n') 164 | run(t, '1. text ', '
      \n
    1. text
    2. \n
    \n') 165 | run(t, '# text ', '

    text

    \n') 166 | run(t, '> text ', '
    \n

    text

    \n
    \n') 167 | run(t, '> * text ', { match: /
    / }) 168 | t.end() 169 | }) 170 | 171 | test('tags with spacing:', function (t) { 172 | run(t, '* text ', '
      \n
    • text
    • \n
    \n') 173 | run(t, '* text ', '
      \n
    • text
    • \n
    \n') 174 | t.end() 175 | }) 176 | 177 | test('li with paragraphs:', function (t) { 178 | run(t, '* text\n\n* text', 179 | '
      \n' + 180 | '
    • \n' + 181 | '

      text

      \n' + 182 | '
    • \n' + 183 | '
    • \n' + 184 | '

      text

      \n' + 185 | '
    • \n' + 186 | '
    \n') 187 | t.end() 188 | }) 189 | 190 | test('line breaks:', function (t) { 191 | run(t, 'para\n', '

    para

    \n') 192 | run(t, '# heading\n', '

    heading

    \n') 193 | run(t, '> bquote\n', '
    \n

    bquote

    \n
    \n') 194 | run(t, '> > bquote 2x\n', '
    \n
    \n

    bquote 2x

    \n
    \n
    \n') 195 | run(t, '> > bquote 2x\n', '
    \n
    \n

    bquote 2x

    \n
    \n
    \n') 196 | run(t, '> > bquote 2x\n', '
    \n
    \n

    bquote 2x

    \n
    \n
    \n') 197 | t.end() 198 | }) 199 | 200 | test('headings:', function (t) { 201 | run(t, 202 | '# docpress\n\n\n' + 203 | '> It is good\n\n', 204 | '

    docpress

    \n' + 205 | '\n') 208 | t.end() 209 | }) 210 | 211 | test('fenced code blocks:', function (t) { 212 | run(t, 213 | '```\n' + 214 | 'hello\n' + 215 | '```\n' + 216 | '', 217 | '
    hello\n' +
    218 |     '
    \n') 219 | t.end() 220 | }) 221 | 222 | test('indented blocks:', function (t) { 223 | run(t, 224 | ' hello\n' + 225 | '\n' + 226 | '', 227 | '
    hello\n' +
    228 |     '
    \n') 229 | t.end() 230 | }) 231 | 232 | test('inline code:', function (t) { 233 | run(t, 234 | 'hi `there` ', 235 | '

    hi there

    \n') 236 | 237 | run(t, 238 | 'hi *there* ', 239 | '

    hi there

    \n') 240 | t.end() 241 | }) 242 | 243 | test('silent errors:', function (t) { 244 | run(t, 245 | 'hi ', 246 | '

    hi

    \n') 247 | t.end() 248 | }) 249 | -------------------------------------------------------------------------------- /test/support/test.js: -------------------------------------------------------------------------------- 1 | function test (md, t, input, output) { 2 | if (typeof output === 'object' && output.match) { 3 | t.ok(md.render(input).match(output.match), input) 4 | } else { 5 | t.equal(md.render(input), output, input) 6 | } 7 | } 8 | 9 | module.exports = test 10 | --------------------------------------------------------------------------------
    / }) 157 | run(t, '| x | y |\n|---|---|\n| a | b |\n', { match: /^