├── .gitignore ├── .npmignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── package.json ├── playground ├── bundle.js ├── dev-server.sh ├── flex.css ├── index.coffee └── index.html ├── src ├── index.coffee └── preprocess.coffee ├── test └── md2react-test.coffee └── testem.yml /.gitignore: -------------------------------------------------------------------------------- 1 | ### https://raw.github.com/github/gitignore/d866fb556184cc1edffd9d0f1ca205fe1916a7f6/Node.gitignore 2 | 3 | # Logs 4 | logs 5 | *.log 6 | 7 | # Runtime data 8 | pids 9 | *.pid 10 | *.seed 11 | 12 | # Directory for instrumented libs generated by jscoverage/JSCover 13 | lib-cov 14 | 15 | # Coverage directory used by tools like istanbul 16 | coverage 17 | 18 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 19 | .grunt 20 | 21 | # node-waf configuration 22 | .lock-wscript 23 | 24 | # Compiled binary addons (http://nodejs.org/api/addons.html) 25 | build/Release 26 | 27 | # Dependency directory 28 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git- 29 | node_modules 30 | lib/ 31 | test/*.js 32 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | src/ 2 | test/ 3 | playground/ 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # ChangeLog 2 | 3 | ## v0.4.0 4 | 5 | - Start changelog 6 | - Update mdast to v0.11 7 | - Support tasklist 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 mizchi 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 13 | all 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 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # md2react [](https://circleci.com/gh/mizchi/md2react/tree/master) 2 | 3 | ``` 4 | npm install --save md2react 5 | ``` 6 | 7 | See [md2react playground](http://mizchi.github.io/md2react/ "md2react playground") 8 | 9 | ## Example 10 | 11 | ```javascript 12 | global.React = require('react'); 13 | var md2react = require('md2react'); 14 | 15 | var md = '# Hello md2react'; 16 | var html = React.renderToString(md2react(md)); 17 | 18 | /* 19 |
a
b
c
d1 | 18 |d2 | 19 | 20 |
...` are often broken up into a series of HTML nodes. 7 | # Put them back together into a single "raw" HTML node. 8 | convertScatteredPreToRawHTML(root, sourceText) 9 | 10 | # Formalize HTML tags. 11 | root.children = decomposeHTMLNodes(root.children) 12 | root.children = foldHTMLNodes(root.children) 13 | 14 | # Process footnotes and and links. 15 | if options.footnotes 16 | mapping = defineFootnoteNumber(root).mapping 17 | applyFootnoteNumber(root, mapping) 18 | defs = removeDefinitions(root) 19 | if options.footnotes 20 | appendFootnoteDefinitionCollection(root, defs) 21 | 22 | # Sanitize HTML tags. 23 | root = wrapHTMLNodeInParagraph(root) 24 | root = sanitizeTag(root) 25 | 26 | [root, defs] 27 | 28 | # Sets `footnoteNumber` property to every footnote reference node. 29 | # Footnote number starts at 1 and is incremented whenever a new footnote 30 | # identifier appears. Returns `{mapping, maxNumber}` 31 | # 32 | # Example (Markdown): 33 | # 34 | # first footnote[^foo] # footnoteNumber for [^foo] is 1 35 | # second footnote[^bar] # footnoteNumber for [^bar] is 2 36 | # use first footnote again[^foo] # footnoteNumber for [^foo] is 1 37 | # yet another footnote[^qux] # footnoteNumber for [^qux] is 3 38 | defineFootnoteNumber = (node, num = 1, mapping = {}) -> 39 | for child in node.children 40 | if child.type is 'footnoteReference' 41 | id = child.identifier 42 | unless mapping[id]? 43 | mapping[id] = num 44 | num += 1 45 | child.footnoteNumber = mapping[id] 46 | if child.children 47 | num = defineFootnoteNumber(child, num, mapping).maxNumber 48 | {mapping, maxNumber: num} 49 | 50 | # Sets `footnoteNumber` property to every footnote definition node using a 51 | # given identifier-to-number mapping. `footnoteNumber` of a definition with 52 | # undefined identifier will be 0. 53 | # 54 | # Example (Markdown): 55 | # 56 | # Given mapping = `{"foo": 1, "bar": 2, "qux": 3}`, 57 | # 58 | # [^bar]: this is bar # footnoteNumber for this node is 2 59 | # [^foo]: this is foo # footnoteNumber for this node is 1 60 | # [^qux]: this is qux # footnoteNumber for this node is 3 61 | # [^xxx]: this is undef # footnoteNumber for this node is 0 62 | applyFootnoteNumber = (node, mapping) -> 63 | return unless node.children? 64 | for child in node.children 65 | # Workaround: 66 | # Footnote definition nodes are supposed to have `footnoteDefinition` type 67 | # but mdast v0.24.0 classifies them as `definition` type if their body 68 | # doesn't contain whitespace (e.g. `[^foo]: body_without_space`). 69 | isFootnoteDefLike = child.type is 'definition' and /^[^]/.test(child.identifier) 70 | if child.type is 'footnoteDefinition' or isFootnoteDefLike 71 | id = if isFootnoteDefLike then child.identifier.slice(1) else child.identifier 72 | child.footnoteNumber = mapping[id] || 0 73 | applyFootnoteNumber(child, mapping) 74 | 75 | # Appends a `footnoteDefinitionCollection` node to `node.children` if `defs` 76 | # contains one or more footnote definition nodes which `footnoteNumber` is > 0. 77 | # Otherwise, do nothing. 78 | # Elements of the collection are sorted by their `footnoteNumber` in ascending 79 | # order. 80 | appendFootnoteDefinitionCollection = (node, defs) -> 81 | footnoteDefs = (def for def in defs when def.footnoteNumber? and def.footnoteNumber > 0) 82 | footnoteDefs.sort (a, b) -> 83 | a.footnoteNumber - b.footnoteNumber 84 | if footnoteDefs.length > 0 85 | node.children.push({type: 'footnoteDefinitionCollection', children: footnoteDefs}) 86 | 87 | # Removes all footnote or link definition nodes from a given AST and returns 88 | # removed nodes. 89 | removeDefinitions = (node) -> 90 | return [] unless node.children? 91 | children = [] 92 | defs = [] 93 | for child in node.children 94 | if child.type in ['definition', 'footnoteDefinition'] 95 | defs.push(child) 96 | else 97 | childDefs = removeDefinitions(child) 98 | Array::push.apply(defs, childDefs) 99 | children.push(child) 100 | node.children = children 101 | defs 102 | 103 | convertPreToRawHTML = (root) -> 104 | for node in root.children 105 | if node.type is 'html' and /^
][^]*<\/pre>$/i.test(node.value) 106 | node.subtype = 'raw' 107 | 108 | convertScatteredPreToRawHTML = (root, sourceText) -> 109 | preTexts = [] 110 | startPreNode = null 111 | startParaIndex = null 112 | sourceLines = null 113 | 114 | for node, i in root.children 115 | isStart = ( 116 | node.type is 'html' and 117 | /^]/i.test(node.value) 118 | ) 119 | if isStart 120 | startPreNode = node 121 | startParaIndex = i 122 | 123 | paraLastNode = null 124 | isEnd = ( 125 | startPreNode? and 126 | node.type is 'html' and 127 | /<\/pre>$/i.test(node.value) 128 | ) or ( 129 | startPreNode? and 130 | node.type is 'paragraph' and 131 | (paraLastNode = node.children[node.children.length - 1]) and 132 | paraLastNode.type is 'html' and 133 | /<\/pre>$/i.test(paraLastNode.value) 134 | ) 135 | if isEnd 136 | endPreNode = paraLastNode ? node 137 | sourceLines ?= sourceText.split(/^/m) # split lines _preserving newline character_ 138 | sliceStart = startIndexFromPosition(startPreNode.position, sourceLines) 139 | sliceEnd = endIndexFromPosition(endPreNode.position, sourceLines) 140 | rawHTML = sourceText.slice(sliceStart, sliceEnd) 141 | preTexts.push 142 | startParaIndex: startParaIndex 143 | paraCount: i - startParaIndex + 1 144 | rawHTML: rawHTML 145 | startPreNode = null 146 | startParaIndex = null 147 | 148 | offset = 0 149 | for pre in preTexts 150 | rawHTMLNode = {type: 'html', subtype: 'raw', value: pre.rawHTML} 151 | start = pre.startParaIndex - offset 152 | root.children.splice(start, pre.paraCount, rawHTMLNode) 153 | offset = pre.paraCount - 1 154 | 155 | startIndexFromPosition = (pos, lines) -> 156 | index = 0 157 | for i in [0...(pos.start.line - 1)] 158 | index += lines[i].length 159 | index += pos.start.column - 1 160 | index 161 | 162 | endIndexFromPosition = (pos, lines) -> 163 | index = 0 164 | for i in [0...(pos.end.line - 1)] 165 | index += lines[i].length 166 | index += pos.end.column - 1 167 | index 168 | 169 | # Returns nodes by converting each occurrence of a series of nodes enclosed by 170 | # a "start" and an "end" HTML node into one "folded" HTML node. A folded node 171 | # has `folded` subtype and two additional properties, `startTag` and `endTag`. 172 | # Its children are the enclosed nodes. 173 | foldHTMLNodes = (nodes) -> 174 | processedNodes = [] 175 | for node in nodes 176 | if node.subtype is 'end' 177 | startTagIndex = null 178 | for pNode, index in processedNodes 179 | if pNode.subtype is 'start' and pNode.tagName is node.tagName 180 | startTagIndex = index 181 | if !startTagIndex? 182 | processedNodes.push(node) 183 | continue 184 | startTag = processedNodes[startTagIndex] 185 | children = processedNodes.splice(startTagIndex).slice(1) 186 | folded = 187 | type: 'html' 188 | subtype: 'folded' 189 | tagName: startTag.tagName 190 | startTag: startTag 191 | endTag: node 192 | children: children 193 | processedNodes.push(folded) 194 | else 195 | if node.children? 196 | node.children = foldHTMLNodes(node.children) 197 | processedNodes.push(node) 198 | processedNodes 199 | 200 | # Decomposes HTML nodes in a given array of nodes and their children. Sets 201 | # `subtype` of an HTML node to `malformed` if the node could not be decomposed. 202 | decomposeHTMLNodes = (nodes) -> 203 | processedNodes = [] 204 | for node in nodes 205 | if node.type is 'html' and node.subtype is 'raw' 206 | processedNodes.push(node) 207 | else if node.type is 'html' 208 | fragmentNodes = decomposeHTMLNode(node) 209 | if fragmentNodes? 210 | processedNodes.push(fragmentNodes...) 211 | else 212 | node.subtype = 'malformed' 213 | processedNodes.push(node) 214 | else 215 | if node.children? 216 | node.children = decomposeHTMLNodes(node.children) 217 | processedNodes.push(node) 218 | processedNodes 219 | 220 | # Decomposes a given HTML node into text nodes and "simple" HTML nodes. 221 | # If decomposition failed, returns null. 222 | # 223 | # mdast can emit "complex" HTML node whose value is like "text". 224 | # Take this value as an example, this method breaks it down to three nodes: 225 | # HTML start tag node ""; text node "text"; and HTML end tag node "". 226 | # 227 | # Each decomposed HTML node has the `subtype` property. 228 | # See `createNodeFromHTMLFragment()` for the possible values. 229 | decomposeHTMLNode = (node) -> 230 | value = node.value 231 | # mdast may insert "\n\n" between adjacent HTML tags. 232 | if node.position.start.line is node.position.end.line 233 | value = value.replace(/\n\n/, '') 234 | fragments = decomposeHTMLString(value) 235 | fragments?.map(createNodeFromHTMLFragment) 236 | 237 | # Splits a given string into an array where each element is ether an HTML tag 238 | # or a string which doesn't contain angle brackets, then returns the array. 239 | # If a given string contains lone angle brackets, returns null. 240 | # 241 | # Example: 242 | # Given "foobar
baz" 243 | # Returns ["foo", "", "bar", "
", "", "baz"] 244 | # Given " oops >_< " 245 | # Returns null 246 | decomposeHTMLString = (str) -> 247 | if str is '' 248 | return null 249 | matches = str.match(/<[^>]*>|[^<>]+/g) 250 | sumLength = matches.reduce(((len, s) -> len+s.length), 0) 251 | if sumLength isnt str.length 252 | null 253 | else 254 | matches 255 | 256 | createNodeFromHTMLFragment = (str) -> 257 | if /^[^<]/.test(str) 258 | return { 259 | type: 'text' 260 | value: str 261 | } 262 | [..., slash, name] = /^<(\/?)([0-9A-Z]+)/i.exec(str) ? [] 263 | subtype = 264 | if !name? 265 | 'special' 266 | else if slash is '/' 267 | 'end' 268 | else if isVoidElement(name) 269 | 'void' 270 | else 271 | 'start' 272 | type: 'html' 273 | subtype: subtype 274 | tagName: name 275 | value: str 276 | 277 | isVoidElement = (elementName) -> 278 | voidElementNames = ['area', 'base', 'br', 'col', 'command', 'embed', 'hr', 'img', 'input', 'keygen', 'link', 'meta', 'param', 'source', 'track', 'wbr'] 279 | voidElementNames.indexOf(elementName) != -1 280 | 281 | # Wraps each top-level HTML node in a paragraph node. 282 | # 283 | # mdast wil put an HTML node directly in `root.children` without wrapping in a 284 | # paragraph node if we write some block element in Markdown: 285 | # 286 | # Markdown: 287 | #foo 288 | # 289 | # bar290 | # 291 | # AST: 292 | # root.children = [ { type: "html", value: "foo\nbar" } ] 293 | # 294 | # However, we disallow writing block element tag in Markdown and convert such 295 | # an HTML node into a series of text nodes by using `sanitizeTag()`. 296 | # To form a paragraph node which have such text nodes as children, we wrap 297 | # top-level HTML nodes in paragraph nodes before applying `sanitizeTag()`. 298 | wrapHTMLNodeInParagraph = (root) -> 299 | children = [] 300 | for child in root.children 301 | if child.type is 'html' 302 | children.push({type: 'paragraph', children: [child]}) 303 | else 304 | children.push(child) 305 | root.children = children 306 | root 307 | 308 | # A subset of [phrasing content] tags, plus RP and RT. 309 | # We rejected some phrasing content tags from the set because it is able 310 | # to write a semantically incorrect HTML with them, which leads to a crash of 311 | # React. 312 | # 313 | # Rejected tags are kinds of: 314 | # 315 | # - embedded contents like IFRAME, MATH, AUDIO, and VIDEO, except for IMG; 316 | # - interactive contents like BUTTON, KEYGEN, and PROGRESS; 317 | # 318 | # [phrasing content]: http://www.w3.org/TR/2011/WD-html5-20110525/content-models.html#phrasing-content-0 319 | ALLOWED_TAG_NAMES = [ 320 | 'a', 'abbr', 'b', 'br', 'cite', 'code', 'del', 'dfn', 'em', 'i', 'img', 321 | 'input', 'ins', 'kbd', 'mark', 'ruby', 'rp', 'rt', 'q', 's', 'samp', 'small', 322 | 'span', 'strong', 'sub', 'sup', 'u', 'wbr', 323 | ] 324 | 325 | # Flatten a disallowed kind of folded tag node into a series of nodes. 326 | # 327 | # Example: 328 | # node.children = [ { type: 'html, subtype: 'folded', 329 | # startTag: startTag, endTag: endTag, children: [child1, child2] }, ... ] 330 | # # ^this is flattend into: 331 | # node.children = [ startTag, child1, child2, endTag, ... ] 332 | # 333 | # startTag and endTag are now freestanding, so they will be rendered as invalid HTML tags. 334 | sanitizeTag = (node) -> 335 | return node unless node.children? 336 | children = [] 337 | for child in node.children 338 | if child.subtype is 'folded' and child.tagName not in ALLOWED_TAG_NAMES 339 | children.push(child.startTag) 340 | Array::push.apply(children, sanitizeTag(child).children) 341 | children.push(child.endTag) 342 | else 343 | children.push(sanitizeTag(child)) 344 | node.children = children 345 | node 346 | 347 | module.exports = preprocess 348 | -------------------------------------------------------------------------------- /test/md2react-test.coffee: -------------------------------------------------------------------------------- 1 | should = chai.should() 2 | global.React = require 'react' 3 | md2react = require '../src/index' 4 | 5 | options = gfm: true, breaks: true, tasklist: true, footnotes: true 6 | 7 | describe 'text', -> 8 | 9 | it 'should be compiled', -> 10 | React.renderToStaticMarkup md2react 'foo', options 11 | .should.equal '' 12 | 13 | describe 'escape', -> 14 | 15 | it 'should be compiled', -> 16 | React.renderToStaticMarkup md2react '\\', options 17 | .should.equal 'foo
' 18 | 19 | describe 'break', -> 20 | 21 | it 'should be compiled', -> 22 | React.renderToStaticMarkup md2react ''' 23 | foo 24 | bar 25 | baz 26 | ''' 27 | , options 28 | .should.equal '\\
' 29 | 30 | describe 'horizontalRule', -> 31 | 32 | it 'should be compiled', -> 33 | React.renderToStaticMarkup md2react ''' 34 | --- 35 | ''', options 36 | .should.equal 'foo
bar
baz' 37 | 38 | describe 'image', -> 39 | 40 | it 'should be compiled', -> 41 | React.renderToStaticMarkup md2react ''' 42 |  43 | ''', options 44 | .should.equal '' 45 | 46 | describe 'inlineCode', -> 47 | 48 | it 'should be compiled', -> 49 | React.renderToStaticMarkup md2react ''' 50 | `var a = 100;` 51 | ''', options 52 | .should.equal '
' 53 | 54 | describe 'code', -> 55 | 56 | it 'should be compiled', -> 57 | React.renderToStaticMarkup md2react ''' 58 | ``` 59 | var a = 100; 60 | var b = 200; 61 | 62 | var c = 300; 63 | ``` 64 | ''', options 65 | .should.equal '
var a = 100;
' 66 | 67 | describe 'root', -> 68 | 69 | it 'should be compiled', -> 70 | React.renderToStaticMarkup md2react '', options 71 | .should.equal '' 72 | 73 | it 'should be compiled when node has children', -> 74 | React.renderToStaticMarkup md2react '', options 75 | .should.equal '' 76 | 77 | describe 'strong', -> 78 | 79 | it 'should be compiled', -> 80 | React.renderToStaticMarkup md2react '**foo**', options 81 | .should.equal 'var a = 100;\nvar b = 200;\n\nvar c = 300;
' 82 | 83 | it 'should be compiled when node has children', -> 84 | React.renderToStaticMarkup md2react '**foo~~bar~~baz**', options 85 | .should.equal 'foo
' 86 | 87 | describe 'emphasis', -> 88 | 89 | it 'should be compiled', -> 90 | React.renderToStaticMarkup md2react '*foo*', options 91 | .should.equal 'foo
barbaz' 92 | 93 | it 'should be compiled when node has children', -> 94 | React.renderToStaticMarkup md2react '*foo~~bar~~baz*', options 95 | .should.equal 'foo
' 96 | 97 | describe 'delete', -> 98 | 99 | it 'should be compiled', -> 100 | React.renderToStaticMarkup md2react '~~foo~~', options 101 | .should.equal 'foo
barbaz' 102 | 103 | it 'should be compiled when node has children', -> 104 | React.renderToStaticMarkup md2react '~~foo**bar**baz~~', options 105 | .should.equal '
foo' 106 | 107 | describe 'paragraph', -> 108 | 109 | it 'should be compiled', -> 110 | React.renderToStaticMarkup md2react 'foo', options 111 | .should.equal '
foobarbaz' 112 | 113 | it 'should be compiled when node has children', -> 114 | React.renderToStaticMarkup md2react 'foo', options 115 | .should.equal 'foo
' 116 | 117 | describe 'link', -> 118 | 119 | it 'should be compiled', -> 120 | React.renderToStaticMarkup md2react '[foo](http://example.com)', options 121 | .should.equal '' 122 | 123 | it 'should be compiled when node has children', -> 124 | React.renderToStaticMarkup md2react '[foo**bar**baz](http://example.com)', options 125 | .should.equal '' 126 | 127 | describe 'heading', -> 128 | 129 | it 'should be compiled', -> 130 | React.renderToStaticMarkup md2react ''' 131 | # heading1 132 | ## heading2 133 | ### heading3 134 | #### heading4 135 | ##### heading5 136 | ###### heading6 137 | ''', options 138 | .should.equal 'foo
' 139 | 140 | it 'should be compiled when node has children', -> 141 | React.renderToStaticMarkup md2react ''' 142 | # foo**bar**baz 143 | ## foo**bar**baz 144 | ### foo**bar**baz 145 | #### foo**bar**baz 146 | ##### foo**bar**baz 147 | ###### foo**bar**baz 148 | ''', options 149 | .should.equal 'heading1
heading2
heading3
heading4
heading5
heading6
' 150 | 151 | describe 'list', -> 152 | 153 | it 'should be compiled', -> 154 | React.renderToStaticMarkup md2react ''' 155 | - foo 156 | - [ ] bar 157 | - [x] baz 158 | 1. foo 159 | 1. bar 160 | 1. baz 161 | ''', options 162 | .should.equal 'foobarbaz
foobarbaz
foobarbaz
foobarbaz
foobarbaz
foobarbaz
' 163 | 164 | it 'should be compiled when node has children', -> 165 | React.renderToStaticMarkup md2react ''' 166 | - foo**bar**baz 167 | - [ ] bar*baz*qux 168 | - [x] bar~~qux~~quux 169 | 1. foo**bar**baz 170 | 1. bar*baz*qux 171 | 1. bar~~qux~~quux 172 | ''', options 173 | .should.equal '
foo
bar
baz
foo
bar
baz
' 174 | 175 | describe 'blockquote', -> 176 | 177 | it 'should be compiled', -> 178 | React.renderToStaticMarkup md2react ''' 179 | > foo 180 | bar 181 | baz 182 | ''', options 183 | .should.equal '
foobarbaz
barbazqux
bar
quxquux
foobarbaz
barbazqux
bar
quxquux' 184 | 185 | it 'should be compiled when node has children', -> 186 | React.renderToStaticMarkup md2react ''' 187 | > foo**bar**baz 188 | bar*baz*qux 189 | baz~~qux~~quux 190 | ''', options 191 | .should.equal 'foo
bar
baz' 192 | 193 | describe 'linkReference', -> 194 | 195 | it 'should be compiled', -> 196 | React.renderToStaticMarkup md2react ''' 197 | [foo][Example] 198 | [bar][example] 199 | [Example] 200 | [example]: http://example.com 201 | ''', options 202 | .should.equal '' 203 | 204 | it 'should be compiled when node has children', -> 205 | React.renderToStaticMarkup md2react ''' 206 | [foo**bar**baz][Example] 207 | [foo**bar**baz][example] 208 | [Example] 209 | [example]: http://example.com 210 | ''', options 211 | .should.equal '' 212 | 213 | describe 'footnotes', -> 214 | 215 | it 'should be compiled', -> 216 | React.renderToStaticMarkup md2react ''' 217 | a[^foo]a[^foo]b[^bar] 218 | [^foo]: description1 219 | [^bar]: description2 220 | ''', options 221 | .should.equal '' 222 | 223 | it 'should be compiled when node has children', -> 224 | React.renderToStaticMarkup md2react ''' 225 | a[^foo]a[^foo]b[^bar] 226 | [^foo]: description**1** 227 | [^bar]: description[2](http://example.com) 228 | ''', options 229 | .should.equal '' 230 | 231 | describe 'table', -> 232 | 233 | it 'should be compiled', -> 234 | React.renderToStaticMarkup md2react ''' 235 | | foo | bar | baz | 236 | |:----|:---:|----:| 237 | | 1 | 2 | 3 | 238 | ''', options 239 | .should.equal 'foobarbaz
barbazqux
bazquxquux' 240 | 241 | describe 'html', -> 242 | 243 | it 'should be compiled', -> 244 | React.renderToStaticMarkup md2react ''' 245 | foo 246 | ''', options 247 | .should.equal '
foo bar baz 1 2 3 ' 248 | 249 | describe 'complex', -> 250 | 251 | it 'should be compiled', -> 252 | md = ''' 253 | # Hello 254 | hello 255 | 256 | - a 257 | - b 258 | 259 | ---- 260 | 261 | - [ ] unchecked 262 | - [x] checked 263 | - plain 264 | 265 | ------- 266 | 267 | 1. 1 268 | 2. 2 269 | 270 | ------- 271 | 272 | - [x] a 273 | - [ ] b 274 | - c 275 | 276 | ------ 277 | 278 | `a` 279 | 280 | ~~striked~~ 281 | 282 | 283 | 284 | 285 | ``` 286 | bbb 287 | ``` 288 | 289 | **AA** 290 | 291 | *BB* 292 | 293 | [foo](/foo) 294 | 295 |  296 | 297 | > aaa 298 | > bbb 299 | 300 | 301 | | TH | TH | 302 | | ---- | ---- | 303 | | TD | TD | 304 | | TD | TD | 305 | 306 | - 307 | loose 308 | 309 | - item 310 | 311 | ''' 312 | element = md2react md, gfm: true, breaks: true, tasklist: true 313 | React.renderToStaticMarkup element 314 | .should.equal '' 315 | -------------------------------------------------------------------------------- /testem.yml: -------------------------------------------------------------------------------- 1 | framework: mocha+chai 2 | before_tests: npm run build-test 3 | src_files: 4 | - test/*.coffee 5 | serve_files: 6 | - test/bundle.js 7 | launch_in_dev: 8 | - Chrome 9 | launch_in_ci: 10 | - Chrome 11 | --------------------------------------------------------------------------------foo