Something went wrong. Try again.
Markdown parser fork with extended syntax for personal use.
Something went wrong. Try again.
17 kB · 766 lines
Rust
at hack
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767use markdown::{mdast::Node, message::Message, Constructs, ParseOptions};use test_utils::swc::{parse_esm, parse_expression};mod test_utils;
#[allow(unused)]#[derive(Debug)]enum Error { Mdast(Message), Serde(serde_json::Error),}
#[test]#[cfg(feature = "serde")]fn serde_constructs() -> Result<(), Error> { use pretty_assertions::assert_eq;
assert_eq!( serde_json::to_string(&Constructs::default()).unwrap(), r#"{"attention":true,"autolink":true,"blockQuote":true,"characterEscape":true,"characterReference":true,"codeIndented":true,"codeFenced":true,"codeText":true,"definition":true,"frontmatter":false,"gfmAutolinkLiteral":false,"gfmFootnoteDefinition":false,"gfmLabelStartFootnote":false,"gfmStrikethrough":false,"gfmTable":false,"gfmTaskListItem":false,"hardBreakEscape":true,"hardBreakTrailing":true,"headingAtx":true,"headingSetext":true,"htmlFlow":true,"htmlText":true,"labelStartImage":true,"labelStartLink":true,"labelEnd":true,"listItem":true,"mathFlow":false,"mathText":false,"mdxEsm":false,"mdxExpressionFlow":false,"mdxExpressionText":false,"mdxJsxFlow":false,"mdxJsxText":false,"thematicBreak":true}"# );
Ok(())}
#[test]#[cfg(feature = "serde")]fn serde_compile_options() -> Result<(), Error> { use pretty_assertions::assert_eq;
assert_eq!( serde_json::to_string(&markdown::CompileOptions::gfm()).unwrap(), r#"{"allowAnyImgSrc":false,"allowDangerousHtml":false,"allowDangerousProtocol":false,"defaultLineEnding":"\n","gfmFootnoteBackLabel":null,"gfmFootnoteClobberPrefix":null,"gfmFootnoteLabelAttributes":null,"gfmFootnoteLabelTagName":null,"gfmFootnoteLabel":null,"gfmTaskListItemCheckable":false,"gfmTagfilter":true}"# );
Ok(())}
#[test]#[cfg(feature = "serde")]fn serde_parse_options() -> Result<(), Error> { use pretty_assertions::assert_eq;
assert_eq!( serde_json::to_string(&ParseOptions::gfm()).unwrap(), r#"{"constructs":{"attention":true,"autolink":true,"blockQuote":true,"characterEscape":true,"characterReference":true,"codeIndented":true,"codeFenced":true,"codeText":true,"definition":true,"frontmatter":false,"gfmAutolinkLiteral":true,"gfmFootnoteDefinition":true,"gfmLabelStartFootnote":true,"gfmStrikethrough":true,"gfmTable":true,"gfmTaskListItem":true,"hardBreakEscape":true,"hardBreakTrailing":true,"headingAtx":true,"headingSetext":true,"htmlFlow":true,"htmlText":true,"labelStartImage":true,"labelStartLink":true,"labelEnd":true,"listItem":true,"mathFlow":false,"mathText":false,"mdxEsm":false,"mdxExpressionFlow":false,"mdxExpressionText":false,"mdxJsxFlow":false,"mdxJsxText":false,"thematicBreak":true},"gfmStrikethroughSingleTilde":true,"mathTextSingleDollar":true}"# );
Ok(())}
#[test]fn serde_blockquote() -> Result<(), Error> { assert_serde( "> a", r#"{ "type": "root", "children": [ { "type": "blockquote", "children": [ { "type": "paragraph", "children": [{"type": "text", "value": "a"}] } ] } ]}"#, ParseOptions::default(), )}
#[test]fn serde_footnote_definition() -> Result<(), Error> { assert_serde( "[^a]: b", r#"{ "type": "root", "children": [ { "type": "footnoteDefinition", "identifier": "a", "label": "a", "children": [ { "type": "paragraph", "children": [{"type": "text", "value": "b"}] } ] } ]}"#, ParseOptions::gfm(), )}
#[test]fn serde_mdx_jsx_flow_element() -> Result<(), Error> { let options = ParseOptions { mdx_esm_parse: Some(Box::new(parse_esm)), mdx_expression_parse: Some(Box::new(parse_expression)), ..ParseOptions::mdx() };
assert_serde( "<a b c={d} e=\"f\" {...g} />", r#"{ "type": "root", "children": [ { "type": "mdxJsxFlowElement", "name": "a", "attributes": [ {"type": "mdxJsxAttribute", "name": "b"}, { "type": "mdxJsxAttribute", "name": "c", "value": { "_markdownRsStops": [[0, 8]], "type": "mdxJsxAttributeValueExpression", "value": "d" } }, {"type": "mdxJsxAttribute", "name": "e", "value": "f"}, { "_markdownRsStops": [[0, 18]], "type": "mdxJsxExpressionAttribute", "value": "...g" } ], "children": [] } ]}"#, options, )}
#[test]fn serde_list() -> Result<(), Error> { assert_serde( "* a", r#"{ "type": "root", "children": [ { "type": "list", "ordered": false, "spread": false, "children": [ { "type": "listItem", "spread": false, "children": [ { "type": "paragraph", "children": [{"type": "text", "value": "a"}] } ] } ] } ]}"#, ParseOptions::default(), )}
#[test]fn serde_mdxjs_esm() -> Result<(), Error> { let options = ParseOptions { mdx_esm_parse: Some(Box::new(parse_esm)), mdx_expression_parse: Some(Box::new(parse_expression)), ..ParseOptions::mdx() };
assert_serde( "import a, {b} from 'c'", r#"{ "type": "root", "children": [ { "_markdownRsStops": [[0, 0]], "type": "mdxjsEsm", "value": "import a, {b} from 'c'" } ]}"#, options, )}
#[test]fn serde_toml() -> Result<(), Error> { assert_serde( "+++\na: b\n+++", r#"{ "type": "root", "children": [{"type": "toml", "value": "a: b"}]}"#, ParseOptions { constructs: Constructs { frontmatter: true, ..Constructs::default() }, ..ParseOptions::default() }, )}
#[test]fn serde_yaml() -> Result<(), Error> { assert_serde( "---\na: b\n---", r#"{ "type": "root", "children": [{"type": "yaml", "value": "a: b"}]}"#, ParseOptions { constructs: Constructs { frontmatter: true, ..Constructs::default() }, ..ParseOptions::default() }, )}
#[test]fn serde_break() -> Result<(), Error> { assert_serde( "a\\\nb", r#"{ "type": "root", "children": [ { "type": "paragraph", "children": [ {"type": "text", "value": "a"}, {"type": "break"}, {"type": "text", "value": "b"} ] } ]}"#, ParseOptions::default(), )}
#[test]fn serde_inline_code() -> Result<(), Error> { assert_serde( "`a`", r#"{ "type": "root", "children": [ { "type": "paragraph", "children": [{"type": "inlineCode", "value": "a"}] } ]}"#, ParseOptions::default(), )}
#[test]fn serde_inline_math() -> Result<(), Error> { assert_serde( "$a$", r#"{ "type": "root", "children": [ { "type": "paragraph", "children": [{"type": "inlineMath","value": "a"}] } ]}"#, ParseOptions { constructs: Constructs { math_text: true, ..Constructs::default() }, ..ParseOptions::default() }, )}
#[test]fn serde_delete() -> Result<(), Error> { assert_serde( "~~a~~", r#"{ "type": "root", "children": [ { "type": "paragraph", "children": [ { "type": "delete", "children": [{"type": "text","value": "a"}] } ] } ]}"#, ParseOptions::gfm(), )}
#[test]fn serde_emphasis() -> Result<(), Error> { assert_serde( "*a*", r#"{ "type": "root", "children": [ { "type": "paragraph", "children": [ { "type": "emphasis", "children": [{"type": "text","value": "a"}] } ] } ]}"#, ParseOptions::default(), )}
#[test]fn serde_mdx_text_expression() -> Result<(), Error> { let options = ParseOptions { mdx_esm_parse: Some(Box::new(parse_esm)), mdx_expression_parse: Some(Box::new(parse_expression)), ..ParseOptions::mdx() };
assert_serde( "a {b}", r#"{ "type": "root", "children": [ { "type": "paragraph", "children": [ {"type": "text","value": "a "}, { "_markdownRsStops": [[0,3]], "type": "mdxTextExpression", "value": "b" } ] } ]}"#, options, )}
#[test]fn serde_footnote_reference() -> Result<(), Error> { assert_serde( "[^a]\n\n[^a]: b", r#"{ "type": "root", "children": [ { "type": "paragraph", "children": [ {"type": "footnoteReference", "identifier": "a", "label": "a"} ] }, { "type": "footnoteDefinition", "identifier": "a", "label": "a", "children": [ { "type": "paragraph", "children": [{"type": "text", "value": "b"}] } ] } ]}"#, ParseOptions::gfm(), )}
#[test]fn serde_html() -> Result<(), Error> { assert_serde( "<a>", r#"{ "type": "root", "children": [{"type": "html", "value": "<a>"}]}"#, ParseOptions::gfm(), )}
#[test]fn serde_image() -> Result<(), Error> { assert_serde( "", r#"{ "type": "root", "children": [ { "type": "paragraph", "children": [{"type": "image", "url": "b", "alt": "a"}] } ]}"#, ParseOptions::default(), )}
#[test]fn serde_image_reference() -> Result<(), Error> { assert_serde( "![a]\n\n[a]: b", r#"{ "type": "root", "children": [ { "type": "paragraph", "children": [ { "type": "imageReference", "alt": "a", "label": "a", "identifier": "a", "referenceType": "shortcut" } ] }, {"type": "definition", "url": "b", "identifier": "a", "label": "a"} ]}"#, ParseOptions::default(), )}
#[test]fn serde_mdx_jsx_text_element() -> Result<(), Error> { assert_serde( "a <b c />", r#"{ "type": "root", "children": [ { "type": "paragraph", "children": [ {"type": "text", "value": "a "}, { "type": "mdxJsxTextElement", "name": "b", "attributes": [{"type": "mdxJsxAttribute", "name": "c"}], "children": [] } ] } ]}"#, ParseOptions::mdx(), )}
#[test]fn serde_link() -> Result<(), Error> { assert_serde( "[a](b)", r#"{ "type": "root", "children": [ { "type": "paragraph", "children": [ { "type": "link", "url": "b", "children": [{"type": "text", "value": "a"}] } ] } ]}"#, ParseOptions::default(), )}
#[test]fn serde_link_reference() -> Result<(), Error> { assert_serde( "[a]\n\n[a]: b", r#"{ "type": "root", "children": [ { "type": "paragraph", "children": [ { "type": "linkReference", "identifier": "a", "label": "a", "referenceType": "shortcut", "children": [{"type": "text", "value": "a"}] } ] }, { "type": "definition", "url": "b", "identifier": "a", "label": "a" } ]}"#, ParseOptions::default(), )}
#[test]fn serde_strong() -> Result<(), Error> { assert_serde( "**a**", r#"{ "type": "root", "children": [ { "type": "paragraph", "children": [ { "type": "strong", "children": [{"type": "text", "value": "a"}] } ] } ]}"#, ParseOptions::default(), )}
#[test]fn serde_text() -> Result<(), Error> { assert_serde( "a", r#"{ "type": "root", "children": [ {"type": "paragraph", "children": [{"type": "text", "value": "a"}]} ]}"#, ParseOptions::default(), )}
#[test]fn serde_code() -> Result<(), Error> { assert_serde( "~~~js eval\nconsole.log(1)\n~~~", r#"{ "type": "root", "children": [ {"type": "code", "lang": "js", "meta": "eval", "value": "console.log(1)"} ]}"#, ParseOptions::default(), )?;
assert_serde( "```\nconsole.log(1)\n```", r#"{ "type": "root", "children": [{"type": "code", "value": "console.log(1)"}]}"#, ParseOptions::default(), )}
#[test]fn serde_math() -> Result<(), Error> { assert_serde( "$$\n1 + 1 = 2\n$$", r#"{ "type": "root", "children": [{"type": "math", "value": "1 + 1 = 2"}]}"#, ParseOptions { constructs: Constructs { math_flow: true, ..Constructs::default() }, ..ParseOptions::default() }, )}
#[test]fn serde_mdx_flow_expression() -> Result<(), Error> { let options = ParseOptions { mdx_esm_parse: Some(Box::new(parse_esm)), mdx_expression_parse: Some(Box::new(parse_expression)), ..ParseOptions::mdx() };
assert_serde( "{a}", r#"{ "type": "root", "children": [ {"_markdownRsStops": [[0, 1]], "type": "mdxFlowExpression", "value": "a"} ]}"#, options, )}
#[test]fn serde_heading() -> Result<(), Error> { assert_serde( "# a", r#"{ "type": "root", "children": [ { "type": "heading", "depth": 1, "children": [{"type": "text", "value": "a"}] } ]}"#, ParseOptions::default(), )}
#[test]fn serde_table() -> Result<(), Error> { // To do: `"none"` should serialize in serde as `null`. assert_serde( "| a | b | c | d |\n| - | :- | -: | :-: |\n| 1 | 2 | 3 | 4 |", r#"{ "type": "root", "children": [ { "type": "table", "align": [null, "left", "right", "center"], "children": [ { "type": "tableRow", "children": [ {"type": "tableCell", "children": [{"type": "text", "value": "a"}]}, {"type": "tableCell", "children": [{"type": "text", "value": "b"}]}, {"type": "tableCell", "children": [{"type": "text", "value": "c"}]}, {"type": "tableCell", "children": [{"type": "text", "value": "d"}]} ] }, { "type": "tableRow", "children": [ {"type": "tableCell","children": [{"type": "text", "value": "1"}]}, {"type": "tableCell","children": [{"type": "text", "value": "2"}]}, {"type": "tableCell","children": [{"type": "text", "value": "3"}]}, {"type": "tableCell","children": [{"type": "text", "value": "4"}]} ] } ] } ]}"#, ParseOptions::gfm(), )}
#[test]fn serde_thematic_break() -> Result<(), Error> { assert_serde( "***", r#"{"type": "root", "children": [{"type": "thematicBreak"}]}"#, ParseOptions::default(), )}
#[test]fn serde_definition() -> Result<(), Error> { assert_serde( "[a]: b", r###"{ "type": "root", "children": [ {"type": "definition", "url": "b", "identifier": "a", "label": "a"} ]}"###, ParseOptions::default(), )}
#[test]fn serde_paragraph() -> Result<(), Error> { assert_serde( "a", r#"{ "type": "root", "children": [ { "type": "paragraph", "children": [{"type": "text", "value": "a"}] } ]}"#, ParseOptions::default(), )}
/// Assert serde of mdast constructs.////// Refer below links for the mdast JSON construct types./// * <https://github.com/syntax-tree/mdast#nodes>/// * <https://github.com/syntax-tree/mdast-util-mdx#syntax-tree>/// * <https://github.com/syntax-tree/mdast-util-frontmatter#syntax-tree>#[cfg(feature = "serde")]fn assert_serde(input: &str, expected: &str, options: ParseOptions) -> Result<(), Error> { use pretty_assertions::assert_eq;
let mut source = markdown::to_mdast(input, &options).map_err(Error::Mdast)?;
remove_position(&mut source); // Serialize to JSON let actual_value: serde_json::Value = serde_json::to_value(&source).map_err(Error::Serde)?; let expected_value: serde_json::Value = serde_json::from_str(expected).map_err(Error::Serde)?;
// Assert serialization. assert_eq!(actual_value, expected_value);
// Assert deserialization. assert_eq!( source, serde_json::from_value(actual_value).map_err(Error::Serde)? );
Ok(())}
#[cfg(not(feature = "serde"))]#[allow(unused_variables)]fn assert_serde(input: &str, expected: &str, options: ParseOptions) -> Result<(), Error> { Ok(())}
#[allow(dead_code)]fn remove_position(node: &mut Node) { if let Some(children) = node.children_mut() { for child in children { remove_position(child); } }
node.position_set(None);}