diff options
-rw-r--r-- | examples/lib.rs | 8 | ||||
-rw-r--r-- | src/compiler.rs | 6 | ||||
-rw-r--r-- | src/construct/mdx_jsx_text.rs | 47 | ||||
-rw-r--r-- | tests/mdx_jsx_text.rs | 821 |
4 files changed, 867 insertions, 15 deletions
diff --git a/examples/lib.rs b/examples/lib.rs index fe4f9ef..17524b7 100644 --- a/examples/lib.rs +++ b/examples/lib.rs @@ -1,7 +1,7 @@ extern crate micromark; use micromark::{micromark, micromark_with_options, Constructs, Options}; -fn main() { +fn main() -> Result<(), String> { // Turn on debugging. // You can show it with `RUST_LOG=debug cargo run --example lib` env_logger::init(); @@ -32,7 +32,7 @@ fn main() { gfm_tagfilter: true, ..Options::default() } - ) + )? ); // Support other extensions that are not in CommonMark. @@ -47,6 +47,8 @@ fn main() { }, ..Options::default() } - ) + )? ); + + Ok(()) } diff --git a/src/compiler.rs b/src/compiler.rs index 4f0f958..572cc4e 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -364,6 +364,7 @@ fn enter(context: &mut CompileContext) { | Name::HeadingAtxText | Name::HeadingSetextText | Name::Label + | Name::MdxJsxTextTag | Name::ReferenceString | Name::ResourceTitleString => on_enter_buffer(context), @@ -401,7 +402,10 @@ fn enter(context: &mut CompileContext) { /// Handle [`Exit`][Kind::Exit]. fn exit(context: &mut CompileContext) { match context.events[context.index].name { - Name::CodeFencedFenceMeta | Name::MathFlowFenceMeta | Name::Resource => { + Name::CodeFencedFenceMeta + | Name::MathFlowFenceMeta + | Name::MdxJsxTextTag + | Name::Resource => { on_exit_drop(context); } Name::CharacterEscapeValue | Name::CodeTextData | Name::Data | Name::MathTextData => { diff --git a/src/construct/mdx_jsx_text.rs b/src/construct/mdx_jsx_text.rs index 4c71fec..f6981ce 100644 --- a/src/construct/mdx_jsx_text.rs +++ b/src/construct/mdx_jsx_text.rs @@ -553,7 +553,7 @@ pub fn attribute_primary_name_after(tokenizer: &mut Tokenizer) -> State { State::Next(StateName::MdxJsxTextAttributeLocalNameBefore), State::Nok, ); - State::Retry(StateName::MdxJsxTextEsWhitespaceStart) + State::Next(StateName::MdxJsxTextEsWhitespaceStart) } // Initializer: start of an attribute value. Some(b'=') => { @@ -678,7 +678,7 @@ pub fn attribute_local_name_after(tokenizer: &mut Tokenizer) -> State { State::Next(StateName::MdxJsxTextAttributeValueBefore), State::Nok, ); - State::Retry(StateName::MdxJsxTextEsWhitespaceStart) + State::Next(StateName::MdxJsxTextEsWhitespaceStart) } _ => { // End of name. @@ -750,9 +750,10 @@ pub fn attribute_value_quoted_start(tokenizer: &mut Tokenizer) -> State { tokenizer.enter(Name::MdxJsxTextTagAttributeValueLiteralMarker); tokenizer.consume(); tokenizer.exit(Name::MdxJsxTextTagAttributeValueLiteralMarker); - tokenizer.enter(Name::MdxJsxTextTagAttributeValueLiteral); + tokenizer.exit(Name::MdxJsxTextTagAttributeValueLiteral); + tokenizer.exit(Name::MdxJsxTextTagAttribute); tokenizer.attempt( - State::Next(StateName::MdxJsxTextAttributeValueBefore), + State::Next(StateName::MdxJsxTextAttributeBefore), State::Nok, ); State::Next(StateName::MdxJsxTextEsWhitespaceStart) @@ -793,7 +794,7 @@ pub fn attribute_value_quoted(tokenizer: &mut Tokenizer) -> State { || matches!(tokenizer.current, None | Some(b'\n')) { tokenizer.exit(Name::MdxJsxTextTagAttributeValueLiteralValue); - State::Retry(StateName::MdxJsxTextAttributeValueQuoted) + State::Retry(StateName::MdxJsxTextAttributeValueQuotedStart) } else { tokenizer.consume(); State::Next(StateName::MdxJsxTextAttributeValueQuoted) @@ -920,11 +921,13 @@ fn id_cont(code: Option<char>) -> bool { } fn crash(tokenizer: &Tokenizer, at: &str, expect: &str) -> State { + let char = char_after_index(tokenizer.parse_state.bytes, tokenizer.point.index); + // To do: externalize this, and the print mechanism in the tokenizer, // to one proper formatter. - let actual = match tokenizer.current { + let actual = match char { None => "end of file".to_string(), - Some(byte) => format_byte(byte), + Some(char) => format!("character {}", format_char(char)), }; State::Error(format!( @@ -933,10 +936,32 @@ fn crash(tokenizer: &Tokenizer, at: &str, expect: &str) -> State { )) } +fn format_char(char: char) -> String { + let unicode = format!("U+{:>04X}", char as u32); + let printable = match char { + '`' => Some("`` ` ``".to_string()), + ' '..='~' => Some(format!("`{}`", char)), + _ => None, + }; + + if let Some(char) = printable { + format!("{} ({})", char, unicode) + } else { + unicode + } +} + fn format_byte(byte: u8) -> String { - match byte { - b'`' => "`` ` ``".to_string(), - b' '..=b'~' => format!("`{}`", str::from_utf8(&[byte]).unwrap()), - _ => format!("character U+{:>04X}", byte), + let unicode = format!("U+{:>04X}", byte); + let printable = match byte { + b'`' => Some("`` ` ``".to_string()), + b' '..=b'~' => Some(format!("`{}`", str::from_utf8(&[byte]).unwrap())), + _ => None, + }; + + if let Some(char) = printable { + format!("{} ({})", char, unicode) + } else { + unicode } } diff --git a/tests/mdx_jsx_text.rs b/tests/mdx_jsx_text.rs new file mode 100644 index 0000000..1890aad --- /dev/null +++ b/tests/mdx_jsx_text.rs @@ -0,0 +1,821 @@ +extern crate micromark; +use micromark::{micromark_with_options, Constructs, Options}; +use pretty_assertions::assert_eq; + +#[test] +fn mdx_jsx_text_core() -> Result<(), String> { + let mdx = Options { + constructs: Constructs::mdx(), + ..Options::default() + }; + + assert_eq!( + micromark_with_options("a <b> c", &mdx)?, + "<p>a c</p>", + "should support mdx jsx (text) if enabled" + ); + + assert_eq!( + micromark_with_options("a <b/> c.", &mdx)?, + "<p>a c.</p>", + "should support a self-closing element" + ); + + assert_eq!( + micromark_with_options("a <b></b> c.", &mdx)?, + "<p>a c.</p>", + "should support a closed element" + ); + + assert_eq!( + micromark_with_options("a <></> c.", &mdx)?, + "<p>a c.</p>", + "should support fragments" + ); + + assert_eq!( + micromark_with_options("a <b>*b*</b> c.", &mdx)?, + "<p>a <em>b</em> c.</p>", + "should support markdown inside elements" + ); + + Ok(()) +} + +#[test] +fn mdx_jsx_text_agnosic() -> Result<(), String> { + let mdx = Options { + constructs: Constructs::mdx(), + ..Options::default() + }; + + assert_eq!( + micromark_with_options("a <b /> c", &mdx)?, + "<p>a c</p>", + "should support a self-closing element" + ); + + assert_eq!( + micromark_with_options("a <b> c </b> d", &mdx)?, + "<p>a c d</p>", + "should support a closed element" + ); + + assert_eq!( + micromark_with_options("a <b> c", &mdx)?, + "<p>a c</p>", + "should support an unclosed element" + ); + + // To do: expressions. + // assert_eq!( + // micromark_with_options("a <b {1 + 1} /> c", &mdx)?, + // "<p>a c</p>", + // "should support an attribute expression" + // ); + + // To do: expressions. + // assert_eq!( + // micromark_with_options("a <b c={1 + 1} /> d", &mdx)?, + // "<p>a d</p>", + // "should support an attribute value expression" + // ); + + Ok(()) +} + +#[test] +fn mdx_jsx_text_gnostic() -> Result<(), String> { + let mdx = Options { + constructs: Constructs::mdx(), + ..Options::default() + }; + + assert_eq!( + micromark_with_options("a <b /> c", &mdx)?, + "<p>a c</p>", + "should support a self-closing element" + ); + + assert_eq!( + micromark_with_options("a <b> c </b> d", &mdx)?, + "<p>a c d</p>", + "should support a closed element" + ); + + assert_eq!( + micromark_with_options("a <b> c", &mdx)?, + "<p>a c</p>", + "should support an unclosed element" + ); + + // To do: expressions. + // assert_eq!( + // micromark_with_options("a <b {...c} /> d", &mdx)?, + // "<p>a d</p>", + // "should support an attribute expression" + // ); + + // To do: expressions. + // assert_eq!( + // micromark_with_options("a <b {...{c: 1, d: Infinity, e: false}} /> f", &mdx)?, + // "<p>a f</p>", + // "should support more complex attribute expression (1)" + // ); + + // To do: expressions. + // assert_eq!( + // micromark_with_options("a <b {...[1, Infinity, false]} /> d", &mdx)?, + // "<p>a d</p>", + // "should support more complex attribute expression (2)" + // ); + + // To do: expressions. + // assert_eq!( + // micromark_with_options("a <b c={1 + 1} /> d", &mdx)?, + // "<p>a d</p>", + // "should support an attribute value expression" + // ); + + // To do: expressions. + // assert_eq!( + // micromark_with_options("a <b c={} /> d", &mdx) + // .err() + // .unwrap(), + // "Unexpected empty expression", + // "should crash on an empty attribute value expression" + // ); + + // To do: expressions. + // assert_eq!( + // micromark_with_options("a <b {1 + 1} /> c", &mdx) + // .err() + // .unwrap(), + // "Could not parse expression with acorn: Unexpected token", + // "should crash on a non-spread attribute expression" + // ); + + // To do: expressions. + // assert_eq!( + // micromark_with_options("a <b c={?} /> d", &mdx) + // .err() + // .unwrap(), + // "Could not parse expression with acorn: Unexpected token", + // "should crash on invalid JS in an attribute value expression" + // ); + + // // To do: expressions. + // assert_eq!( + // micromark_with_options("a <b {?} /> c", &mdx) + // .err() + // .unwrap(), + // "Could not parse expression with acorn: Unexpected token", + // "should crash on invalid JS in an attribute expression" + // ); + + // // To do: expressions. + // assert_eq!( + // micromark_with_options("a <b{c=d}={}/> f", &mdx) + // .err() + // .unwrap(), + // "Unexpected `ExpressionStatement` in code: expected an object spread", + // "should crash on invalid JS in an attribute expression (2)" + // ); + + // To do: expressions. + // assert_eq!( + // micromark_with_options("a <b c={(2)} d={<e />} /> f", &mdx)?, + // "<p>a f</p>", + // "should support parenthesized expressions" + // ); + + Ok(()) +} + +#[test] +fn mdx_jsx_text_complete() -> Result<(), String> { + let mdx = Options { + constructs: Constructs::mdx(), + ..Options::default() + }; + + assert_eq!( + micromark_with_options("a <b> c", &mdx)?, + "<p>a c</p>", + "should support an unclosed element" + ); + + assert_eq!( + micromark_with_options("a <> c", &mdx)?, + "<p>a c</p>", + "should support an unclosed fragment" + ); + + assert_eq!( + micromark_with_options("a < \t>b</>", &mdx)?, + "<p>a < \t>b</p>", + "should *not* support whitespace in the opening tag (fragment)" + ); + + assert_eq!( + micromark_with_options("a < \nb\t>b</b>", &mdx)?, + "<p>a <\nb\t>b</p>", + "should *not* support whitespace in the opening tag (named)" + ); + + assert_eq!( + micromark_with_options("a <!> b", &mdx) + .err() + .unwrap(), + "1:4: Unexpected character `!` (U+0021) before name, expected a character that can start a name, such as a letter, `$`, or `_` (note: to create a comment in MDX, use `{/* text */}`)", + "should crash on a nonconforming start identifier" + ); + + assert_eq!( + micromark_with_options("a </(> b.", &mdx) + .err() + .unwrap(), + "1:5: Unexpected character `(` (U+0028) before name, expected a character that can start a name, such as a letter, `$`, or `_`", + "should crash on a nonconforming start identifier in a closing tag" + ); + + assert_eq!( + micromark_with_options("a <π /> b.", &mdx)?, + "<p>a b.</p>", + "should support non-ascii identifier start characters" + ); + + assert_eq!( + micromark_with_options("a <© /> b.", &mdx) + .err() + .unwrap(), + "1:4: Unexpected character U+00A9 before name, expected a character that can start a name, such as a letter, `$`, or `_`", + "should crash on non-conforming non-ascii identifier start characters" + ); + + assert_eq!( + micromark_with_options("a <!--b-->", &mdx) + .err() + .unwrap(), + "1:4: Unexpected character `!` (U+0021) before name, expected a character that can start a name, such as a letter, `$`, or `_` (note: to create a comment in MDX, use `{/* text */}`)", + "should crash nicely on what might be a comment" + ); + + assert_eq!( + micromark_with_options("a <// b\nc/>", &mdx) + .err() + .unwrap(), + "1:5: Unexpected character `/` (U+002F) before name, expected a character that can start a name, such as a letter, `$`, or `_` (note: JS comments in JSX tags are not supported in MDX)", + "should crash nicely on JS line comments inside tags (1)" + ); + + assert_eq!( + micromark_with_options("a <b// c\nd/>", &mdx) + .err() + .unwrap(), + "1:6: Unexpected character `/` (U+002F) after self-closing slash, expected `>` to end the tag (note: JS comments in JSX tags are not supported in MDX)", + "should crash nicely JS line comments inside tags (2)" + ); + + assert_eq!( + micromark_with_options("a </*b*/c>", &mdx) + .err() + .unwrap(), + "1:5: Unexpected character `*` (U+002A) before name, expected a character that can start a name, such as a letter, `$`, or `_`", + "should crash nicely JS multiline comments inside tags (1)" + ); + + assert_eq!( + micromark_with_options("a <b/*c*/>", &mdx) + .err() + .unwrap(), + "1:6: Unexpected character `*` (U+002A) after self-closing slash, expected `>` to end the tag", + "should crash nicely JS multiline comments inside tags (2)" + ); + + assert_eq!( + micromark_with_options("a <a\u{200C}b /> b.", &mdx)?, + "<p>a b.</p>", + "should support non-ascii identifier continuation characters" + ); + + assert_eq!( + micromark_with_options("a <a¬ /> b.", &mdx) + .err() + .unwrap(), + "1:5: Unexpected character U+00AC in name, expected a name character such as letters, digits, `$`, or `_`; whitespace before attributes; or the end of the tag", + "should crash on non-conforming non-ascii identifier continuation characters" + ); + + assert_eq!( + micromark_with_options("a <b@c.d>", &mdx) + .err() + .unwrap(), + "1:5: Unexpected character `@` (U+0040) in name, expected a name character such as letters, digits, `$`, or `_`; whitespace before attributes; or the end of the tag (note: to create a link in MDX, use `[text](url)`)", + "should crash nicely on what might be an email link" + ); + + assert_eq!( + micromark_with_options("a <a-->b</a-->.", &mdx)?, + "<p>a b.</p>", + "should support dashes in names" + ); + + assert_eq!( + micromark_with_options("a <a?> c.", &mdx) + .err() + .unwrap(), + "1:5: Unexpected character `?` (U+003F) in name, expected a name character such as letters, digits, `$`, or `_`; whitespace before attributes; or the end of the tag", + "should crash on nonconforming identifier continuation characters" + ); + + assert_eq!( + micromark_with_options("a <abc . def.ghi>b</abc.def . ghi>.", &mdx)?, + "<p>a b.</p>", + "should support dots in names for method names" + ); + + assert_eq!( + micromark_with_options("a <b.c@d.e>", &mdx) + .err() + .unwrap(), + "1:7: Unexpected character `@` (U+0040) in member name, expected a name character such as letters, digits, `$`, or `_`; whitespace before attributes; or the end of the tag (note: to create a link in MDX, use `[text](url)`)", + "should crash nicely on what might be an email link in member names" + ); + + assert_eq!( + micromark_with_options("a <svg: rect>b</ svg :rect>.", &mdx)?, + "<p>a b.</p>", + "should support colons in names for local names" + ); + + assert_eq!( + micromark_with_options("a <a:+> c.", &mdx) + .err() + .unwrap(), + "1:6: Unexpected character `+` (U+002B) before local name, expected a character that can start a name, such as a letter, `$`, or `_` (note: to create a link in MDX, use `[text](url)`)", + "should crash on a nonconforming character to start a local name" + ); + + assert_eq!( + micromark_with_options("a <http://example.com>", &mdx) + .err() + .unwrap(), + "1:9: Unexpected character `/` (U+002F) before local name, expected a character that can start a name, such as a letter, `$`, or `_` (note: to create a link in MDX, use `[text](url)`)", + "should crash nicely on what might be a protocol in local names" + ); + + assert_eq!( + micromark_with_options("a <http: >", &mdx) + .err() + .unwrap(), + "1:10: Unexpected character `>` (U+003E) before local name, expected a character that can start a name, such as a letter, `$`, or `_`", + "should crash nicely on what might be a protocol in local names" + ); + + assert_eq!( + micromark_with_options("a <a:b|> c.", &mdx) + .err() + .unwrap(), + "1:7: Unexpected character `|` (U+007C) in local name, expected a name character such as letters, digits, `$`, or `_`; whitespace before attributes; or the end of the tag", + "should crash on a nonconforming character in a local name" + ); + + assert_eq!( + micromark_with_options("a <a..> c.", &mdx) + .err() + .unwrap(), + "1:6: Unexpected character `.` (U+002E) before member name, expected a character that can start an attribute name, such as a letter, `$`, or `_`; whitespace before attributes; or the end of the tag", + "should crash on a nonconforming character to start a member name" + ); + + assert_eq!( + micromark_with_options("a <a.b,> c.", &mdx) + .err() + .unwrap(), + "1:7: Unexpected character `,` (U+002C) in member name, expected a name character such as letters, digits, `$`, or `_`; whitespace before attributes; or the end of the tag", + "should crash on a nonconforming character in a member name" + ); + + assert_eq!( + micromark_with_options("a <a:b .> c.", &mdx) + .err() + .unwrap(), + "1:8: Unexpected character `.` (U+002E) after local name, expected a character that can start an attribute name, such as a letter, `$`, or `_`; whitespace before attributes; or the end of the tag", + "should crash on a nonconforming character after a local name" + ); + + assert_eq!( + micromark_with_options("a <a.b :> c.", &mdx) + .err() + .unwrap(), + "1:8: Unexpected character `:` (U+003A) after member name, expected a character that can start an attribute name, such as a letter, `$`, or `_`; whitespace before attributes; or the end of the tag", + "should crash on a nonconforming character after a member name" + ); + + assert_eq!( + micromark_with_options("a <a => c.", &mdx) + .err() + .unwrap(), + "1:6: Unexpected character `=` (U+003D) after name, expected a character that can start an attribute name, such as a letter, `$`, or `_`; whitespace before attributes; or the end of the tag", + "should crash on a nonconforming character after name" + ); + + // To do: expressions. + // assert_eq!( + // micromark_with_options("a <b {...props} {...rest}>c</b>.", &mdx)?, + // "<p>a c.</p>", + // "should support attribute expressions" + // ); + + // To do: expressions. + // assert_eq!( + // micromark_with_options("a <b {...{\"a\": \"b\"}}>c</b>.", &mdx)?, + // "<p>a c.</p>", + // "should support nested balanced braces in attribute expressions" + // ); + + // To do: expressions. + // assert_eq!( + // micromark_with_options("<a{...b}/>.", &mdx)?, + // "<p>.</p>", + // "should support attribute expressions directly after a name" + // ); + + // To do: expressions. + // assert_eq!( + // micromark_with_options("<a.b{...c}/>.", &mdx)?, + // "<p>.</p>", + // "should support attribute expressions directly after a member name" + // ); + + // To do: expressions. + // assert_eq!( + // micromark_with_options("<a:b{...c}/>.", &mdx)?, + // "<p>.</p>", + // "should support attribute expressions directly after a local name" + // ); + + // To do: expressions. + // assert_eq!( + // micromark_with_options("a <b c{...d}/>.", &mdx)?, + // "<p>a .</p>", + // "should support attribute expressions directly after boolean attributes" + // ); + + // To do: expressions. + // assert_eq!( + // micromark_with_options("a <b c:d{...e}/>.", &mdx)?, + // "<p>a .</p>", + // "should support attribute expressions directly after boolean qualified attributes" + // ); + + // To do: expressions. + // assert_eq!( + // micromark_with_options("a <b a {...props} b>c</b>.", &mdx)?, + // "<p>a c.</p>", + // "should support attribute expressions and normal attributes" + // ); + + assert_eq!( + micromark_with_options("a <b c d=\"d\"\t\tefg=\"e\">c</b>.", &mdx)?, + "<p>a c.</p>", + "should support attributes" + ); + + // To do: expressions. + // assert_eq!( + // micromark_with_options("a <b {...p}~>c</b>.", &mdx) + // .err() + // .unwrap(), + // "Unexpected character `~` (U+007E) before attribute name, expected a character that can start an attribute name, such as a letter, `$`, or `_`; whitespace before attributes; or the end of the tag", + // "should crash on a nonconforming character before an attribute name" + // ); + + // To do: expressions. + // assert_eq!( + // micromark_with_options("a <b {...", &mdx) + // .err() + // .unwrap(), + // "Unexpected end of file in expression, expected a corresponding closing brace for `{`", + // "should crash on a missing closing brace in attribute expression" + // ); + + assert_eq!( + micromark_with_options("a <a b@> c.", &mdx) + .err() + .unwrap(), + "1:7: Unexpected character `@` (U+0040) in attribute name, expected an attribute name character such as letters, digits, `$`, or `_`; `=` to initialize a value; whitespace before attributes; or the end of the tag", + "should crash on a nonconforming character in attribute name" + ); + + assert_eq!( + micromark_with_options("a <b xml :\tlang\n= \"de-CH\" foo:bar>c</b>.", &mdx)?, + "<p>a c.</p>", + "should support prefixed attributes" + ); + + assert_eq!( + micromark_with_options("a <b a b : c d : e = \"f\" g/>.", &mdx)?, + "<p>a .</p>", + "should support prefixed and normal attributes" + ); + + assert_eq!( + micromark_with_options("a <a b 1> c.", &mdx) + .err() + .unwrap(), + "1:8: Unexpected character `1` (U+0031) after attribute name, expected a character that can start an attribute name, such as a letter, `$`, or `_`; `=` to initialize a value; or the end of the tag", + "should crash on a nonconforming character after an attribute name" + ); + + assert_eq!( + micromark_with_options("a <a b:#> c.", &mdx) + .err() + .unwrap(), + "1:8: Unexpected character `#` (U+0023) before local attribute name, expected a character that can start an attribute name, such as a letter, `$`, or `_`; `=` to initialize a value; or the end of the tag", + "should crash on a nonconforming character to start a local attribute name" + ); + + assert_eq!( + micromark_with_options("a <a b:c%> c.", &mdx) + .err() + .unwrap(), + "1:9: Unexpected character `%` (U+0025) in local attribute name, expected an attribute name character such as letters, digits, `$`, or `_`; `=` to initialize a value; whitespace before attributes; or the end of the tag", + "should crash on a nonconforming character in a local attribute name" + ); + + assert_eq!( + micromark_with_options("a <a b:c ^> c.", &mdx) + .err() + .unwrap(), + "1:10: Unexpected character `^` (U+005E) after local attribute name, expected a character that can start an attribute name, such as a letter, `$`, or `_`; `=` to initialize a value; or the end of the tag", + "should crash on a nonconforming character after a local attribute name" + ); + + // To do: expressions. + // assert_eq!( + // micromark_with_options("a <b c={1 + 1}>c</b>.", &mdx)?, + // "<p>a c.</p>", + // "should support attribute value expressions" + // ); + + // To do: expressions. + // assert_eq!( + // micromark_with_options("a <b c={1 + ({a: 1}).a}>c</b>.", &mdx)?, + // "<p>a c.</p>", + // "should support nested balanced braces in attribute value expressions" + // ); + + assert_eq!( + micromark_with_options("a <a b=``> c.", &mdx) + .err() + .unwrap(), + "1:8: Unexpected character `` ` `` (U+0060) before attribute value, expected a character that can start an attribute value, such as `\"`, `'`, or `{`", + "should crash on a nonconforming character before an attribute value" + ); + + assert_eq!( + micromark_with_options("a <a b=<c />> d.", &mdx) + .err() + .unwrap(), + "1:8: Unexpected character `<` (U+003C) before attribute value, expected a character that can start an attribute value, such as `\"`, `'`, or `{` (note: to use an element or fragment as a prop value in MDX, use `{<element />}`)", + "should crash nicely on what might be a fragment, element as prop value" + ); + + assert_eq!( + micromark_with_options("a <a b=\"> c.", &mdx) + .err() + .unwrap(), + "1:13: Unexpected end of file in attribute value, expected a corresponding closing quote `\"` (U+0022)", + "should crash on a missing closing quote in double quoted attribute value" + ); + + assert_eq!( + micromark_with_options("a <a b=\"> c.", &mdx) + .err() + .unwrap(), + "1:13: Unexpected end of file in attribute value, expected a corresponding closing quote `\"` (U+0022)", + "should crash on a missing closing quote in single quoted attribute value" + ); + + // To do: expressions. + // assert_eq!( + // micromark_with_options("a <a b={> c.", &mdx) + // .err() + // .unwrap(), + // "Unexpected end of file in expression, expected a corresponding closing brace for `{`", + // "should crash on a missing closing brace in an attribute value expression" + // ); + + assert_eq!( + micromark_with_options("a <a b=\"\"*> c.", &mdx) + .err() + .unwrap(), + "1:10: Unexpected character `*` (U+002A) before attribute name, expected a character that can start an attribute name, such as a letter, `$`, or `_`; whitespace before attributes; or the end of the tag", + "should crash on a nonconforming character after an attribute value" + ); + + assert_eq!( + micromark_with_options("<a b=\"\"c/>.", &mdx)?, + "<p>.</p>", + "should support an attribute directly after a value" + ); + + // To do: expressions. + // assert_eq!( + // micromark_with_options("<a{...b}c/>.", &mdx)?, + // "<p>.</p>", + // "should support an attribute directly after an attribute expression" + // ); + + assert_eq!( + micromark_with_options("a <a/b> c.", &mdx) + .err() + .unwrap(), + "1:6: Unexpected character `b` (U+0062) after self-closing slash, expected `>` to end the tag", + "should crash on a nonconforming character after a self-closing slash" + ); + + assert_eq!( + micromark_with_options("<a/ \t>.", &mdx)?, + "<p>.</p>", + "should support whitespace directly after closing slash" + ); + + assert_eq!( + micromark_with_options("a > c.", &mdx).err(), + None, + "should *not* crash on closing angle in text" + ); + + assert_eq!( + micromark_with_options("a <>`<`</> c.", &mdx).err(), + None, + "should *not* crash on opening angle in tick code in an element" + ); + + assert_eq!( + micromark_with_options("a <>`` ``` ``</>", &mdx).err(), + None, + "should *not* crash on ticks in tick code in an element" + ); + + assert_eq!( + micromark_with_options("a </> c.", &mdx)?, + "<p>a c.</p>", + "should support a closing tag w/o open elements" + ); + + assert_eq!( + micromark_with_options("a <></b>", &mdx)?, + "<p>a </p>", + "should support mismatched tags (1)" + ); + assert_eq!( + micromark_with_options("a <b></>", &mdx)?, + "<p>a </p>", + "should support mismatched tags (2)" + ); + assert_eq!( + micromark_with_options("a <a.b></a>", &mdx)?, + "<p>a </p>", + "should support mismatched tags (3)" + ); + assert_eq!( + micromark_with_options("a <a></a.b>", &mdx)?, + "<p>a </p>", + "should support mismatched tags (4)" + ); + assert_eq!( + micromark_with_options("a <a.b></a.c>", &mdx)?, + "<p>a </p>", + "should support mismatched tags (5)" + ); + assert_eq!( + micromark_with_options("a <a:b></a>", &mdx)?, + "<p>a </p>", + "should support mismatched tags (6)" + ); + assert_eq!( + micromark_with_options("a <a></a:b>", &mdx)?, + "<p>a </p>", + "should support mismatched tags (7)" + ); + assert_eq!( + micromark_with_options("a <a:b></a:c>", &mdx)?, + "<p>a </p>", + "should support mismatched tags (8)" + ); + assert_eq!( + micromark_with_options("a <a:b></a.b>", &mdx)?, + "<p>a </p>", + "should support mismatched tags (9)" + ); + + assert_eq!( + micromark_with_options("a <a>b</a/>", &mdx)?, + "<p>a b</p>", + "should support a closing self-closing tag" + ); + + assert_eq!( + micromark_with_options("a <a>b</a b>", &mdx)?, + "<p>a b</p>", + "should support a closing tag w/ attributes" + ); + + assert_eq!( + micromark_with_options("a <>b <>c</> d</>.", &mdx)?, + "<p>a b c d.</p>", + "should support nested tags" + ); + + assert_eq!( + micromark_with_options( + "<x y=\"Character references can be used: ", ', <, >, {, and }, they can be named, decimal, or hexadecimal: © ≠ 𝌆\" />.", + &mdx + )?, + "<p>.</p>", + "should support character references in attribute values" + ); + + assert_eq!( + micromark_with_options( + "<x>Character references can be used: ", ', <, >, {, and }, they can be named, decimal, or hexadecimal: © ≠ 𝌆</x>.", + &mdx + )?, + "<p>Character references can be used: ", ', <, >, {, and }, they can be named, decimal, or hexadecimal: © ≠ 𝌆.</p>", + "should support character references in text" + ); + + assert_eq!( + micromark_with_options("<x />.", &mdx)?, + "<p>.</p>", + "should support as text if the closing tag is not the last thing" + ); + + assert_eq!( + micromark_with_options("a <x />", &mdx)?, + "<p>a </p>", + "should support as text if the opening is not the first thing" + ); + + assert_eq!( + micromark_with_options("a *open <b> close* </b> c.", &mdx)?, + "<p>a <em>open close</em> c.</p>", + "should not care about precedence between attention (emphasis)" + ); + + assert_eq!( + micromark_with_options("a **open <b> close** </b> c.", &mdx)?, + "<p>a <strong>open close</strong> c.</p>", + "should not care about precedence between attention (strong)" + ); + + assert_eq!( + micromark_with_options("a [open <b> close](c) </b> d.", &mdx)?, + "<p>a <a href=\"c\">open close</a> d.</p>", + "should not care about precedence between label (link)" + ); + + assert_eq!( + micromark_with_options("a ![open <b> close](c) </b> d.", &mdx)?, + "<p>a <img src=\"c\" alt=\"open close\" /> d.</p>", + "should not care about precedence between label (image)" + ); + + assert_eq!( + micromark_with_options("> a <b>\n> c </b> d.", &mdx)?, + "<blockquote>\n<p>a \nc d.</p>\n</blockquote>", + "should support line endings in elements" + ); + + assert_eq!( + micromark_with_options("> a <b c=\"d\ne\" /> f", &mdx)?, + "<blockquote>\n<p>a f</p>\n</blockquote>", + "should support line endings in attribute values" + ); + + // To do: expressions. + // assert_eq!( + // micromark_with_options("> a <b c={d\ne} /> f", &mdx)?, + // "<blockquote>\n<p>a f</p>\n</blockquote>", + // "should support line endings in attribute value expressions" + // ); + + // To do: expressions. + // assert_eq!( + // micromark_with_options("> a <b {c\nd} /> e", &mdx)?, + // "<blockquote>\n<p>a e</p>\n</blockquote>", + // "should support line endings in attribute expressions" + // ); + + assert_eq!( + micromark_with_options("1 < 3", &mdx)?, + "<p>1 < 3</p>", + "should allow `<` followed by markdown whitespace as text in markdown" + ); + + Ok(()) +} |