Markdown parser fork with extended syntax for personal use.
at main 472 lines 16 kB view raw
1mod test_utils; 2use markdown::{ 3 mdast::{ 4 AttributeContent, AttributeValue, AttributeValueExpression, Blockquote, MdxFlowExpression, 5 MdxJsxAttribute, MdxJsxTextElement, MdxTextExpression, Node, Paragraph, Root, Text, 6 }, 7 message, to_html_with_options, to_mdast, 8 unist::Position, 9 Constructs, Options, ParseOptions, 10}; 11use pretty_assertions::assert_eq; 12use test_utils::swc::{parse_esm, parse_expression}; 13 14/// Note: these tests are also in `micromark/micromark-extension-mdx-expression` 15/// at `tests/index.js`. 16#[test] 17fn mdx_expression_flow_agnostic() -> Result<(), message::Message> { 18 let mdx = Options { 19 parse: ParseOptions::mdx(), 20 ..Default::default() 21 }; 22 23 assert_eq!( 24 to_html_with_options("{a}", &mdx)?, 25 "", 26 "should support an expression" 27 ); 28 29 assert_eq!( 30 to_html_with_options("{}", &mdx)?, 31 "", 32 "should support an empty expression" 33 ); 34 35 // Note: in MDX, indented code is turned off: 36 assert_eq!( 37 to_html_with_options( 38 " {}", 39 &Options { 40 parse: ParseOptions { 41 constructs: Constructs { 42 mdx_expression_flow: true, 43 ..Default::default() 44 }, 45 ..Default::default() 46 }, 47 ..Default::default() 48 } 49 )?, 50 "<pre><code>{}\n</code></pre>", 51 "should prefer indented code over expressions if it’s enabled" 52 ); 53 54 assert_eq!( 55 to_html_with_options( 56 " {}", 57 &Options { 58 parse: ParseOptions { 59 constructs: Constructs { 60 mdx_expression_flow: true, 61 ..Default::default() 62 }, 63 ..Default::default() 64 }, 65 ..Default::default() 66 } 67 )?, 68 "", 69 "should support indented expressions if indented code is enabled" 70 ); 71 72 assert_eq!( 73 to_html_with_options("{a", &mdx).err().unwrap().to_string(), 74 "1:3: Unexpected end of file in expression, expected a corresponding closing brace for `{` (markdown-rs:unexpected-eof)", 75 "should crash if no closing brace is found (1)" 76 ); 77 78 assert_eq!( 79 to_html_with_options("{b { c }", &mdx) 80 .err() 81 .unwrap() 82 .to_string(), 83 "1:9: Unexpected end of file in expression, expected a corresponding closing brace for `{` (markdown-rs:unexpected-eof)", 84 "should crash if no closing brace is found (2)" 85 ); 86 87 assert_eq!( 88 to_html_with_options("{\n}\na", &mdx)?, 89 "<p>a</p>", 90 "should support a line ending in an expression" 91 ); 92 93 assert_eq!( 94 to_html_with_options("{ a } \t\nb", &mdx)?, 95 "<p>b</p>", 96 "should support expressions followed by spaces" 97 ); 98 99 assert_eq!( 100 to_html_with_options(" { a }\nb", &mdx)?, 101 "<p>b</p>", 102 "should support expressions preceded by spaces" 103 ); 104 105 assert_eq!( 106 to_html_with_options("a\n\n* b", &mdx)?, 107 "<p>a</p>\n<ul>\n<li>b</li>\n</ul>", 108 "should support lists after non-expressions (wooorm/markdown-rs#11)" 109 ); 110 111 assert_eq!( 112 to_html_with_options("> {a\nb}", &mdx) 113 .err() 114 .unwrap().to_string(), 115 "2:1: Unexpected lazy line in expression in container, expected line to be prefixed with `>` when in a block quote, whitespace when in a list, etc (markdown-rs:unexpected-lazy)", 116 "should not support lazyness (1)" 117 ); 118 119 assert_eq!( 120 to_html_with_options("> a\n{b}", &mdx)?, 121 "<blockquote>\n<p>a</p>\n</blockquote>\n", 122 "should not support lazyness (2)" 123 ); 124 125 assert_eq!( 126 to_html_with_options("> {a}\nb", &mdx)?, 127 "<blockquote>\n</blockquote>\n<p>b</p>", 128 "should not support lazyness (3)" 129 ); 130 131 assert_eq!( 132 to_html_with_options("> {\n> a\nb}", &mdx) 133 .err() 134 .unwrap().to_string(), 135 "3:1: Unexpected lazy line in expression in container, expected line to be prefixed with `>` when in a block quote, whitespace when in a list, etc (markdown-rs:unexpected-lazy)", 136 "should not support lazyness (4)" 137 ); 138 139 assert_eq!( 140 to_mdast("{alpha +\nbravo}", &mdx.parse)?, 141 Node::Root(Root { 142 children: vec![Node::MdxFlowExpression(MdxFlowExpression { 143 value: "alpha +\nbravo".into(), 144 position: Some(Position::new(1, 1, 0, 2, 7, 15)), 145 stops: vec![(0, 1), (7, 8), (8, 9)] 146 })], 147 position: Some(Position::new(1, 1, 0, 2, 7, 15)) 148 }), 149 "should support mdx expressions (flow) as `MdxFlowExpression`s in mdast" 150 ); 151 152 assert_eq!( 153 to_mdast(" {`\n a\n `}", &mdx.parse)?, 154 Node::Root(Root { 155 children: vec![Node::MdxFlowExpression(MdxFlowExpression { 156 value: "`\n a\n`".into(), 157 position: Some(Position::new(1, 3, 2, 3, 5, 15)), 158 stops: vec![(0, 3), (1, 4), (2, 7), (5, 10), (6, 13)] 159 })], 160 position: Some(Position::new(1, 1, 0, 3, 5, 15)) 161 }), 162 "should support indent in `MdxFlowExpression` in mdast" 163 ); 164 165 Ok(()) 166} 167 168/// Note: these tests are also in `micromark/micromark-extension-mdx-expression` 169/// at `tests/index.js`. 170#[test] 171fn mdx_expression_flow_gnostic() -> Result<(), message::Message> { 172 let swc = Options { 173 parse: ParseOptions { 174 constructs: Constructs::mdx(), 175 mdx_esm_parse: Some(Box::new(parse_esm)), 176 mdx_expression_parse: Some(Box::new(parse_expression)), 177 ..Default::default() 178 }, 179 ..Default::default() 180 }; 181 182 assert_eq!( 183 to_html_with_options("{a}", &swc)?, 184 "", 185 "should support an expression" 186 ); 187 188 assert_eq!( 189 to_html_with_options("{}", &swc)?, 190 "", 191 "should support an empty expression" 192 ); 193 194 assert_eq!( 195 to_html_with_options("{a", &swc).err().unwrap().to_string(), 196 "1:3: Unexpected end of file in expression, expected a corresponding closing brace for `{` (markdown-rs:unexpected-eof)", 197 "should crash if no closing brace is found (1)" 198 ); 199 200 assert_eq!( 201 to_html_with_options("{b { c }", &swc) 202 .err() 203 .unwrap() 204 .to_string(), 205 "1:9: Could not parse expression with swc: Unexpected content after expression (mdx:swc)", 206 "should crash if no closing brace is found (2)" 207 ); 208 209 assert_eq!( 210 to_html_with_options("{\n}\na", &swc)?, 211 "<p>a</p>", 212 "should support a line ending in an expression" 213 ); 214 215 assert_eq!( 216 to_html_with_options("{ a } \t\nb", &swc)?, 217 "<p>b</p>", 218 "should support expressions followed by spaces" 219 ); 220 221 assert_eq!( 222 to_html_with_options(" { a }\nb", &swc)?, 223 "<p>b</p>", 224 "should support expressions preceded by spaces" 225 ); 226 227 assert_eq!( 228 to_html_with_options(" {`\n a\n `}", &swc)?, 229 "", 230 "should support indented expressions" 231 ); 232 233 assert_eq!( 234 to_html_with_options("a{(b)}c", &swc)?, 235 "<p>ac</p>", 236 "should support expressions padded w/ parens" 237 ); 238 239 assert_eq!( 240 to_html_with_options("a{/* b */ ( (c) /* d */ + (e) )}f", &swc)?, 241 "<p>af</p>", 242 "should support expressions padded w/ parens and comments" 243 ); 244 245 assert_eq!( 246 to_mdast("{`\n\t`}", &swc.parse)?, 247 Node::Root(Root { 248 children: vec![Node::MdxFlowExpression(MdxFlowExpression { 249 value: "`\n `".into(), 250 position: Some(Position::new(1, 1, 0, 2, 7, 6)), 251 stops: vec![(0, 1), (1, 2), (2, 3)] 252 })], 253 position: Some(Position::new(1, 1, 0, 2, 7, 6)) 254 }), 255 "should use correct positional info when tabs are used (1, indent)" 256 ); 257 258 assert_eq!( 259 to_mdast("{`\nalpha\t`}", &swc.parse)?, 260 Node::Root(Root { 261 children: vec![Node::MdxFlowExpression(MdxFlowExpression { 262 value: "`\nalpha\t`".into(), 263 position: Some(Position::new(1, 1, 0, 2, 11, 11)), 264 stops: vec![(0, 1), (1, 2), (2, 3)] 265 })], 266 position: Some(Position::new(1, 1, 0, 2, 11, 11)) 267 }), 268 "should use correct positional info when tabs are used (2, content)" 269 ); 270 271 assert_eq!( 272 to_mdast("> aaa <b c={`\n> d\n> `} /> eee", &swc.parse)?, 273 Node::Root(Root { 274 children: vec![Node::Blockquote(Blockquote { 275 children: vec![Node::Paragraph(Paragraph { 276 children: vec![ 277 Node::Text(Text { 278 value: "aaa ".into(), 279 position: Some(Position::new(1, 4, 3, 1, 8, 7)) 280 }), 281 Node::MdxJsxTextElement(MdxJsxTextElement { 282 children: vec![], 283 name: Some("b".into()), 284 attributes: vec![AttributeContent::Property(MdxJsxAttribute { 285 name: "c".into(), 286 value: Some(AttributeValue::Expression(AttributeValueExpression { 287 value: "`\n d\n`".into(), 288 stops: vec![(0, 13), (1, 14), (2, 19), (6, 23), (7, 27)] 289 })) 290 })], 291 position: Some(Position::new(1, 8, 7, 3, 9, 32)) 292 }), 293 Node::Text(Text { 294 value: " eee".into(), 295 position: Some(Position::new(3, 9, 32, 3, 13, 36)) 296 }) 297 ], 298 position: Some(Position::new(1, 3, 2, 3, 13, 36)) 299 })], 300 position: Some(Position::new(1, 1, 0, 3, 13, 36)) 301 })], 302 position: Some(Position::new(1, 1, 0, 3, 13, 36)) 303 }), 304 "should support template strings in JSX (text) in block quotes" 305 ); 306 307 assert_eq!( 308 to_mdast("> ab {`\n>\t`}", &swc.parse)?, 309 Node::Root(Root { 310 children: vec![Node::Blockquote(Blockquote { 311 children: vec![Node::Paragraph(Paragraph { 312 children: vec![ 313 Node::Text(Text { 314 value: "ab ".into(), 315 position: Some(Position::new(1, 3, 2, 1, 6, 5)) 316 }), 317 Node::MdxTextExpression(MdxTextExpression { 318 value: "`\n`".into(), 319 stops: vec![(0, 6), (1, 7), (2, 10)], 320 position: Some(Position::new(1, 6, 5, 2, 7, 12)) 321 }) 322 ], 323 position: Some(Position::new(1, 3, 2, 2, 7, 12)) 324 })], 325 position: Some(Position::new(1, 1, 0, 2, 7, 12)) 326 })], 327 position: Some(Position::new(1, 1, 0, 2, 7, 12)) 328 }), 329 "should use correct positional when there are virtual spaces due to a block quote" 330 ); 331 332 assert_eq!( 333 to_mdast( 334 "> {`\n> alpha\n> bravo\n> charlie\n> delta\n> `}", 335 &swc.parse 336 )?, 337 Node::Root(Root { 338 children: vec![Node::Blockquote(Blockquote { 339 children: vec![Node::MdxFlowExpression(MdxFlowExpression { 340 value: "`\nalpha\nbravo\ncharlie\n delta\n`".into(), 341 position: Some(Position::new(1, 3, 2, 6, 5, 49)), 342 stops: vec![ 343 (0, 3), 344 (1, 4), 345 (2, 7), 346 (7, 12), 347 (8, 16), 348 (13, 21), 349 (14, 26), 350 (21, 33), 351 (22, 38), 352 (28, 44), 353 (29, 47) 354 ] 355 })], 356 position: Some(Position::new(1, 1, 0, 6, 5, 49)) 357 })], 358 position: Some(Position::new(1, 1, 0, 6, 5, 49)) 359 }), 360 "should keep the correct number of spaces in a blockquote (flow)" 361 ); 362 363 assert_eq!( 364 to_mdast( 365 "> {`\n> alpha\n> bravo\n> charlie\n> delta\n> `}", 366 &swc.parse 367 )?, 368 Node::Root(Root { 369 children: vec![Node::Blockquote(Blockquote { 370 children: vec![Node::MdxFlowExpression(MdxFlowExpression { 371 value: "`\nalpha\nbravo\ncharlie\n delta\n`".into(), 372 position: Some(Position::new(1, 3, 2, 6, 5, 49)), 373 stops: vec![ 374 (0, 3), 375 (1, 4), 376 (2, 7), 377 (7, 12), 378 (8, 16), 379 (13, 21), 380 (14, 26), 381 (21, 33), 382 (22, 38), 383 (28, 44), 384 (29, 47) 385 ] 386 })], 387 position: Some(Position::new(1, 1, 0, 6, 5, 49)) 388 })], 389 position: Some(Position::new(1, 1, 0, 6, 5, 49)) 390 }), 391 "should keep the correct number of spaces in a blockquote (flow)" 392 ); 393 394 // Note: the weird character test has to go in mdxjs-rs. 395 396 Ok(()) 397} 398 399/// Note: these tests are also in `micromark/micromark-extension-mdx-expression` 400/// at `tests/index.js`. 401/// This project includes *all* extensions which means that it can use JSX. 402/// There we test something that does not exist in actual MDX but which is used 403/// by the separate JSX extension. 404#[test] 405fn mdx_expression_spread() -> Result<(), message::Message> { 406 let swc = Options { 407 parse: ParseOptions { 408 constructs: Constructs::mdx(), 409 mdx_esm_parse: Some(Box::new(parse_esm)), 410 mdx_expression_parse: Some(Box::new(parse_expression)), 411 ..Default::default() 412 }, 413 ..Default::default() 414 }; 415 416 assert_eq!( 417 to_html_with_options("<a {...b} />", &swc)?, 418 "", 419 "should support a spread" 420 ); 421 422 assert_eq!( 423 to_html_with_options("<a {b} />", &swc).err().unwrap().to_string(), 424 "1:5: Unexpected prop in spread (such as `{x}`): only a spread is supported (such as `{...x}`) (mdx:swc)", 425 "should crash if not a spread" 426 ); 427 428 assert_eq!( 429 to_html_with_options("<a {...?} />", &swc) 430 .err() 431 .unwrap() 432 .to_string(), 433 "1:13: Could not parse expression with swc: Expression expected (mdx:swc)", 434 "should crash on an incorrect spread" 435 ); 436 437 assert_eq!( 438 to_html_with_options("<a {b=c}={} d>", &swc).err().unwrap().to_string(), 439 "1:5: Unexpected prop in spread (such as `{x}`): only a spread is supported (such as `{...x}`) (mdx:swc)", 440 "should crash on an incorrect spread that looks like an assignment" 441 ); 442 443 assert_eq!( 444 to_html_with_options("<a {...b,c} d>", &swc).err().unwrap().to_string(), 445 "1:5: Unexpected extra content in spread (such as `{...x,y}`): only a single spread is supported (such as `{...x}`) (mdx:swc)", 446 "should crash if a spread and other things" 447 ); 448 449 assert_eq!( 450 to_html_with_options("<a {b=c} />", &swc) 451 .err() 452 .unwrap() 453 .to_string(), 454 "1:12: Could not parse expression with swc: assignment property is invalid syntax (mdx:swc)", 455 "should crash if not an identifier" 456 ); 457 458 // Note: `markdown-rs` has no `allowEmpty`. 459 assert_eq!( 460 to_html_with_options("<a {} />", &swc).err().unwrap().to_string(), 461 "1:9: Unexpected prop in spread (such as `{x}`): only a spread is supported (such as `{...x}`) (mdx:swc)", 462 "should crash on an empty spread" 463 ); 464 465 assert_eq!( 466 to_html_with_options("<a {/* b */} />", &swc).err().unwrap().to_string(), 467 "1:5: Unexpected prop in spread (such as `{x}`): only a spread is supported (such as `{...x}`) (mdx:swc)", 468 "should crash on a comment spread" 469 ); 470 471 Ok(()) 472}