aboutsummaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
authorLibravatar Titus Wormer <tituswormer@gmail.com>2022-09-19 11:17:26 +0200
committerLibravatar Titus Wormer <tituswormer@gmail.com>2022-09-19 11:17:26 +0200
commitfe618ff6e38ec0ed4da72a3935fd9ea64ee1cef5 (patch)
treee1db142b47091ab2a3cb3ed09f52e4218d1355fa /tests
parentd4cc03c65a9657d22c59d50f9b3d38b483362560 (diff)
downloadmarkdown-rs-fe618ff6e38ec0ed4da72a3935fd9ea64ee1cef5.tar.gz
markdown-rs-fe618ff6e38ec0ed4da72a3935fd9ea64ee1cef5.tar.bz2
markdown-rs-fe618ff6e38ec0ed4da72a3935fd9ea64ee1cef5.zip
Add support for parsing MDX ESM, expressions
This commit adds support for hooks that lets a user integrate another parser with `micromark-rs`, to parse ESM and expressions according to a particular grammar (such as a programming language, typically JavaScript). For an example integrating with SWC, see `tests/test_utils/mod.rs`. The integration occurs with two functions passed in `options`: `mdx_expression_parse` and `mdx_esm_parse`. The can signal back to micromark when they are successful, whether there is an error at the end (in which case micromark will try to parse more), or whether there is a syntax error (in which case micromark will crash).
Diffstat (limited to 'tests')
-rw-r--r--tests/code_indented.rs6
-rw-r--r--tests/gfm_table.rs6
-rw-r--r--tests/math_text.rs14
-rw-r--r--tests/mdx_esm.rs241
-rw-r--r--tests/mdx_expression_flow.rs281
-rw-r--r--tests/mdx_expression_text.rs422
-rw-r--r--tests/mdx_jsx_text.rs96
-rw-r--r--tests/mdx_swc.rs47
-rw-r--r--tests/test_utils/mod.rs245
9 files changed, 941 insertions, 417 deletions
diff --git a/tests/code_indented.rs b/tests/code_indented.rs
index 29d8909..bf39fa3 100644
--- a/tests/code_indented.rs
+++ b/tests/code_indented.rs
@@ -167,7 +167,11 @@ fn code_indented() -> Result<(), String> {
"a <?\n ?>",
&Options {
allow_dangerous_html: true,
- ..off.clone()
+ constructs: Constructs {
+ code_indented: false,
+ ..Constructs::default()
+ },
+ ..Options::default()
}
)?,
"<p>a <?\n?></p>",
diff --git a/tests/gfm_table.rs b/tests/gfm_table.rs
index b7f884a..8c46a30 100644
--- a/tests/gfm_table.rs
+++ b/tests/gfm_table.rs
@@ -1037,7 +1037,8 @@ bar
"###,
&Options {
allow_dangerous_html: true,
- ..gfm.clone()
+ constructs: Constructs::gfm(),
+ ..Options::default()
}
)?,
r###"<h1>Grave accents</h1>
@@ -1345,7 +1346,8 @@ b
"###,
&Options {
allow_dangerous_html: true,
- ..gfm.clone()
+ constructs: Constructs::gfm(),
+ ..Options::default()
}
)?,
r###"<h2>Blank line</h2>
diff --git a/tests/math_text.rs b/tests/math_text.rs
index dced393..7b53268 100644
--- a/tests/math_text.rs
+++ b/tests/math_text.rs
@@ -30,7 +30,12 @@ fn math_text() -> Result<(), String> {
"$foo$ $$bar$$",
&Options {
math_text_single_dollar: false,
- ..math.clone()
+ constructs: Constructs {
+ math_text: true,
+ math_flow: true,
+ ..Constructs::default()
+ },
+ ..Options::default()
}
)?,
"<p>$foo$ <code class=\"language-math math-inline\">bar</code></p>",
@@ -133,7 +138,12 @@ fn math_text() -> Result<(), String> {
&Options {
allow_dangerous_html: true,
allow_dangerous_protocol: true,
- ..math.clone()
+ constructs: Constructs {
+ math_text: true,
+ math_flow: true,
+ ..Constructs::default()
+ },
+ ..Options::default()
}
)?,
"<p><a href=\"$\">$</p>",
diff --git a/tests/mdx_esm.rs b/tests/mdx_esm.rs
new file mode 100644
index 0000000..f1ea122
--- /dev/null
+++ b/tests/mdx_esm.rs
@@ -0,0 +1,241 @@
+extern crate micromark;
+mod test_utils;
+use micromark::{micromark_with_options, Constructs, Options};
+use pretty_assertions::assert_eq;
+use test_utils::{parse_esm, parse_expression};
+
+#[test]
+fn mdx_esm() -> Result<(), String> {
+ let swc = Options {
+ constructs: Constructs::mdx(),
+ mdx_esm_parse: Some(Box::new(parse_esm)),
+ mdx_expression_parse: Some(Box::new(parse_expression)),
+ ..Options::default()
+ };
+
+ assert_eq!(
+ micromark_with_options("import a from 'b'\n\nc", &swc)?,
+ "<p>c</p>",
+ "should support an import"
+ );
+
+ assert_eq!(
+ micromark_with_options("export default a\n\nb", &swc)?,
+ "<p>b</p>",
+ "should support an export"
+ );
+
+ assert_eq!(
+ micromark_with_options("impossible", &swc)?,
+ "<p>impossible</p>",
+ "should not support other keywords (`impossible`)"
+ );
+
+ assert_eq!(
+ micromark_with_options("exporting", &swc)?,
+ "<p>exporting</p>",
+ "should not support other keywords (`exporting`)"
+ );
+
+ assert_eq!(
+ micromark_with_options("import.", &swc)?,
+ "<p>import.</p>",
+ "should not support a non-whitespace after the keyword"
+ );
+
+ assert_eq!(
+ micromark_with_options("import('a')", &swc)?,
+ "<p>import('a')</p>",
+ "should not support a non-whitespace after the keyword (import-as-a-function)"
+ );
+
+ assert_eq!(
+ micromark_with_options(" import a from 'b'\n export default c", &swc)?,
+ "<p>import a from 'b'\nexport default c</p>",
+ "should not support an indent"
+ );
+
+ assert_eq!(
+ micromark_with_options("- import a from 'b'\n> export default c", &swc)?,
+ "<ul>\n<li>import a from 'b'</li>\n</ul>\n<blockquote>\n<p>export default c</p>\n</blockquote>",
+ "should not support keywords in containers"
+ );
+
+ assert_eq!(
+ micromark_with_options("import a from 'b'\nexport default c", &swc)?,
+ "",
+ "should support imports and exports in the same “block”"
+ );
+
+ assert_eq!(
+ micromark_with_options("import a from 'b'\n\nexport default c", &swc)?,
+ "",
+ "should support imports and exports in separate “blocks”"
+ );
+
+ assert_eq!(
+ micromark_with_options("a\n\nimport a from 'b'\n\nb\n\nexport default c", &swc)?,
+ "<p>a</p>\n<p>b</p>\n",
+ "should support imports and exports in between other constructs"
+ );
+
+ assert_eq!(
+ micromark_with_options("a\nimport a from 'b'\n\nb\nexport default c", &swc)?,
+ "<p>a\nimport a from 'b'</p>\n<p>b\nexport default c</p>",
+ "should not support import/exports when interrupting paragraphs"
+ );
+
+ assert_eq!(
+ micromark_with_options("import a", &swc).err().unwrap(),
+ "1:9: Could not parse esm with swc: Expected ',', got '<eof>'",
+ "should crash on invalid import/exports (1)"
+ );
+
+ assert_eq!(
+ micromark_with_options("import 1/1", &swc).err().unwrap(),
+ "1:9: Could not parse esm with swc: Expected 'from', got 'numeric literal (1, 1)'",
+ "should crash on invalid import/exports (2)"
+ );
+
+ assert_eq!(
+ micromark_with_options("export {\n a\n} from 'b'\n\nc", &swc)?,
+ "<p>c</p>",
+ "should support line endings in import/exports"
+ );
+
+ assert_eq!(
+ micromark_with_options("export {\n\n a\n\n} from 'b'\n\nc", &swc)?,
+ "<p>c</p>",
+ "should support blank lines in import/exports"
+ );
+
+ assert_eq!(
+ micromark_with_options("import a from 'b'\n*md*?", &swc)
+ .err()
+ .unwrap(),
+ "2:6: Could not parse esm with swc: Unexpected token `?`. Expected this, import, async, function, [ for array literal, { for object literal, @ for decorator, function, class, null, true, false, number, bigint, string, regexp, ` for template literal, (, or an identifier",
+ "should crash on markdown after import/export w/o blank line"
+ );
+
+ assert_eq!(
+ micromark_with_options("export var a = 1\n// b\n/* c */\n\nd", &swc)?,
+ "<p>d</p>",
+ "should support comments in “blocks”"
+ );
+
+ assert_eq!(
+ micromark_with_options("export var a = 1\nvar b\n\nc", &swc)
+ .err()
+ .unwrap(),
+ "2:1: Unexpected statement in code: only import/exports are supported",
+ "should crash on other statements in “blocks”"
+ );
+
+ assert_eq!(
+ micromark_with_options("import ('a')\n\nb", &swc)
+ .err()
+ .unwrap(),
+ "1:1: Unexpected statement in code: only import/exports are supported",
+ "should crash on import-as-a-function with a space `import (x)`"
+ );
+
+ assert_eq!(
+ micromark_with_options("import a from 'b'\nexport {a}\n\nc", &swc)?,
+ "<p>c</p>",
+ "should support a reexport from another import"
+ );
+
+ assert_eq!(
+ micromark_with_options("import a from 'b';\nexport {a};\n\nc", &swc)?,
+ "<p>c</p>",
+ "should support a reexport from another import w/ semicolons"
+ );
+
+ assert_eq!(
+ micromark_with_options("import a from 'b'\nexport {a as default}\n\nc", &swc)?,
+ "<p>c</p>",
+ "should support a reexport default from another import"
+ );
+
+ assert_eq!(
+ micromark_with_options("export var a = () => <b />", &swc)?,
+ "",
+ "should support JSX by default"
+ );
+
+ assert_eq!(
+ micromark_with_options("export {a}\n", &swc)?,
+ "",
+ "should support EOF after EOL"
+ );
+
+ assert_eq!(
+ micromark_with_options("import a from 'b'\n\nexport {a}\n\nc", &swc)?,
+ "<p>c</p>",
+ "should support a reexport from another esm block (1)"
+ );
+
+ assert_eq!(
+ micromark_with_options("import a from 'b'\n\nexport {a}\n\n# c", &swc)?,
+ "<h1>c</h1>",
+ "should support a reexport from another esm block (2)"
+ );
+
+ let cases = vec![
+ ("default", "import a from \"b\""),
+ ("whole", "import * as a from \"b\""),
+ ("destructuring", "import {a} from \"b\""),
+ ("destructuring and rename", "import {a as b} from \"c\""),
+ ("default and destructuring", "import a, {b as c} from \"d\""),
+ ("default and whole", "import a, * as b from \"c\""),
+ ("side-effects", "import \"a\""),
+ ];
+
+ for case in cases {
+ assert_eq!(
+ micromark_with_options(case.1, &swc)?,
+ "",
+ "should support imports: {}",
+ case.0
+ );
+ }
+
+ let cases = vec![
+ ("var", "export var a = \"\""),
+ ("const", "export const a = \"\""),
+ ("let", "export let a = \"\""),
+ ("multiple", "export var a, b"),
+ ("multiple w/ assignment", "export var a = \"a\", b = \"b\""),
+ ("function", "export function a() {}"),
+ ("class", "export class a {}"),
+ ("destructuring", "export var {a} = {}"),
+ ("rename destructuring", "export var {a: b} = {}"),
+ ("array destructuring", "export var [a] = []"),
+ ("default", "export default a = 1"),
+ ("default function", "export default function a() {}"),
+ ("default class", "export default class a {}"),
+ ("aggregate", "export * from \"a\""),
+ ("whole reexport", "export * as a from \"b\""),
+ ("reexport destructuring", "export {a} from \"b\""),
+ (
+ "reexport destructuring w rename",
+ "export {a as b} from \"c\"",
+ ),
+ ("reexport as a default whole", "export {default} from \"b\""),
+ (
+ "reexport default and non-default",
+ "export {default as a, b} from \"c\"",
+ ),
+ ];
+
+ for case in cases {
+ assert_eq!(
+ micromark_with_options(case.1, &swc)?,
+ "",
+ "should support exports: {}",
+ case.0
+ );
+ }
+
+ Ok(())
+}
diff --git a/tests/mdx_expression_flow.rs b/tests/mdx_expression_flow.rs
index 2a66a9d..81a31a7 100644
--- a/tests/mdx_expression_flow.rs
+++ b/tests/mdx_expression_flow.rs
@@ -1,6 +1,8 @@
extern crate micromark;
+mod test_utils;
use micromark::{micromark_with_options, Constructs, Options};
use pretty_assertions::assert_eq;
+use test_utils::{parse_esm, parse_expression};
#[test]
fn mdx_expression_flow_agnostic() -> Result<(), String> {
@@ -82,153 +84,132 @@ fn mdx_expression_flow_agnostic() -> Result<(), String> {
Ok(())
}
-// To do: swc.
-// #[test]
-// fn mdx_expression_flow_gnostic() -> Result<(), String> {
-// assert_eq!(
-// micromark_with_options("{a}", &swc),
-// "",
-// "should support an expression"
-// );
-
-// assert_eq!(
-// micromark_with_options("{}", &swc)?,
-// "",
-// "should support an empty expression"
-// );
-
-// // To do: errors.
-// // t.throws(
-// // () => {
-// // micromark_with_options("{a", &swc);
-// // },
-// // /Unexpected end of file in expression, expected a corresponding closing brace for `{`/,
-// // "should crash if no closing brace is found (1)"
-// // );
-
-// // To do: errors.
-// // t.throws(
-// // () => {
-// // micromark_with_options("{b { c }", &swc);
-// // },
-// // /Could not parse expression with swc: Unexpected content after expression/,
-// // "should crash if no closing brace is found (2)"
-// // );
-
-// assert_eq!(
-// micromark_with_options("{\n}\na", &swc)?,
-// "<p>a</p>",
-// "should support a line ending in an expression"
-// );
-
-// assert_eq!(
-// micromark_with_options("{ a } \t\nb", &swc)?,
-// "<p>b</p>",
-// "should support expressions followed by spaces"
-// );
-
-// assert_eq!(
-// micromark_with_options(" { a }\nb", &swc)?,
-// "<p>b</p>",
-// "should support expressions preceded by spaces"
-// );
-
-// assert_eq!(
-// micromark_with_options(" {`\n a\n `}", &swc)?,
-// "",
-// "should support indented expressions"
-// );
-
-// assert_eq!(
-// micromark_with_options("a{(b)}c", &swc)?,
-// "<p>ac</p>",
-// "should support expressions padded w/ parens"
-// );
-
-// assert_eq!(
-// micromark_with_options("a{/* b */ ( (c) /* d */ + (e) )}f", &swc)?,
-// "<p>af</p>",
-// "should support expressions padded w/ parens and comments"
-// );
-
-// Ok(())
-// }
-
-// To do: move to JSX, actually test spread in expressions?
-// To do: swc.
-// #[test]
-// fn mdx_expression_spread() -> Result<(), String> {
-// // To do: errors.
-// // t.throws(
-// // () => {
-// // micromark_with_options("a {b} c", &swc);
-// // },
-// // /Unexpected `Property` in code: only spread elements are supported/,
-// // "should crash if not a spread"
-// // );
-
-// // To do: errors.
-// // t.throws(
-// // () => {
-// // micromark_with_options("a {...?} c", &swc);
-// // },
-// // /Could not parse expression with swc: Unexpected token/,
-// // "should crash on an incorrect spread"
-// // );
-
-// // To do: errors.
-// // t.throws(
-// // () => {
-// // micromark_with_options("a {...b,c} d", &swc);
-// // },
-// // /Unexpected extra content in spread: only a single spread is supported/,
-// // "should crash if a spread and other things"
-// // );
-
-// assert_eq!(
-// micromark_with_options("a {} b", &swc)?,
-// "<p>a b</p>",
-// "should support an empty spread"
-// );
-
-// // To do: errors.
-// // t.throws(
-// // () => {
-// // micromark_with_options("a {} b", &swc);
-// // },
-// // /Unexpected empty expression/,
-// // "should crash on an empty spread w/ `allowEmpty: false`"
-// // );
-
-// // To do: errors.
-// // t.throws(
-// // () => {
-// // micromark_with_options("{a=b}", &swc);
-// // },
-// // /Could not parse expression with swc: Shorthand property assignments are valid only in destructuring patterns/,
-// // "should crash if not a spread w/ `allowEmpty`"
-// // );
-
-// assert_eq!(
-// micromark_with_options("a {/* b */} c", &swc)?,
-// "<p>a c</p>",
-// "should support a comment spread"
-// );
-
-// // To do: errors.
-// // t.throws(
-// // () => {
-// // micromark_with_options("a {/* b */} c", &swc);
-// // },
-// // /Unexpected empty expression/,
-// // "should crash on a comment spread w/ `allowEmpty: false`"
-// // );
-
-// assert_eq!(
-// micromark_with_options("a {...b} c", &swc)?,
-// "<p>a c</p>",
-// "should support a spread"
-// );
-
-// Ok(())
-// }
+#[test]
+fn mdx_expression_flow_gnostic() -> Result<(), String> {
+ let swc = Options {
+ constructs: Constructs::mdx(),
+ mdx_esm_parse: Some(Box::new(parse_esm)),
+ mdx_expression_parse: Some(Box::new(parse_expression)),
+ ..Options::default()
+ };
+
+ assert_eq!(
+ micromark_with_options("{a}", &swc)?,
+ "",
+ "should support an expression"
+ );
+
+ assert_eq!(
+ micromark_with_options("{}", &swc)?,
+ "",
+ "should support an empty expression"
+ );
+
+ assert_eq!(
+ micromark_with_options("{a", &swc).err().unwrap(),
+ "1:3: Unexpected end of file in expression, expected a corresponding closing brace for `{`",
+ "should crash if no closing brace is found (1)"
+ );
+
+ assert_eq!(
+ micromark_with_options("{b { c }", &swc).err().unwrap(),
+ "1:4: Could not parse expression with swc: Unexpected content after expression",
+ "should crash if no closing brace is found (2)"
+ );
+
+ assert_eq!(
+ micromark_with_options("{\n}\na", &swc)?,
+ "<p>a</p>",
+ "should support a line ending in an expression"
+ );
+
+ assert_eq!(
+ micromark_with_options("{ a } \t\nb", &swc)?,
+ "<p>b</p>",
+ "should support expressions followed by spaces"
+ );
+
+ assert_eq!(
+ micromark_with_options(" { a }\nb", &swc)?,
+ "<p>b</p>",
+ "should support expressions preceded by spaces"
+ );
+
+ assert_eq!(
+ micromark_with_options(" {`\n a\n `}", &swc)?,
+ "",
+ "should support indented expressions"
+ );
+
+ assert_eq!(
+ micromark_with_options("a{(b)}c", &swc)?,
+ "<p>ac</p>",
+ "should support expressions padded w/ parens"
+ );
+
+ assert_eq!(
+ micromark_with_options("a{/* b */ ( (c) /* d */ + (e) )}f", &swc)?,
+ "<p>af</p>",
+ "should support expressions padded w/ parens and comments"
+ );
+
+ Ok(())
+}
+
+#[test]
+fn mdx_expression_spread() -> Result<(), String> {
+ let swc = Options {
+ constructs: Constructs::mdx(),
+ mdx_esm_parse: Some(Box::new(parse_esm)),
+ mdx_expression_parse: Some(Box::new(parse_expression)),
+ ..Options::default()
+ };
+
+ assert_eq!(
+ micromark_with_options("<a {...b} />", &swc)?,
+ "",
+ "should support spreads for attribute expression"
+ );
+
+ assert_eq!(
+ micromark_with_options("<a {b} />", &swc).err().unwrap(),
+ "1:5: Expected a single spread value, such as `...x`",
+ "should crash if not a spread"
+ );
+
+ assert_eq!(
+ micromark_with_options("<a {...?} />", &swc).err().unwrap(),
+ "1:13: Could not parse expression with swc: Unexpected token `?`. Expected this, import, async, function, [ for array literal, { for object literal, @ for decorator, function, class, null, true, false, number, bigint, string, regexp, ` for template literal, (, or an identifier",
+ "should crash on an incorrect spread"
+ );
+
+ assert_eq!(
+ micromark_with_options("<a {...b,c} d>", &swc)
+ .err()
+ .unwrap(),
+ "1:5: Expected a single spread value, such as `...x`",
+ "should crash if a spread and other things"
+ );
+
+ assert_eq!(
+ micromark_with_options("<a {} />", &swc).err().unwrap(),
+ "1:5: Expected a single spread value, such as `...x`",
+ "should crash on an empty spread"
+ );
+
+ assert_eq!(
+ micromark_with_options("<a {a=b} />", &swc).err().unwrap(),
+ "1:12: Could not parse expression with swc: assignment property is invalid syntax",
+ "should crash if not an identifier"
+ );
+
+ assert_eq!(
+ micromark_with_options("<a {/* b */} />", &swc)
+ .err()
+ .unwrap(),
+ "1:5: Expected a single spread value, such as `...x`",
+ "should crash on a comment spread"
+ );
+
+ Ok(())
+}
diff --git a/tests/mdx_expression_text.rs b/tests/mdx_expression_text.rs
index b42faf2..3a48965 100644
--- a/tests/mdx_expression_text.rs
+++ b/tests/mdx_expression_text.rs
@@ -1,147 +1,144 @@
extern crate micromark;
+mod test_utils;
use micromark::{micromark_with_options, Constructs, Options};
use pretty_assertions::assert_eq;
+use test_utils::{parse_esm, parse_expression};
-// To do: swc.
-// #[test]
-// fn mdx_expression_text_gnostic_core() -> Result<(), String> {
-// assert_eq!(
-// micromark_with_options("a {} b", &swc)?,
-// "<p>a b</p>",
-// "should support an empty expression (1)"
-// );
-
-// assert_eq!(
-// micromark_with_options("a { \t\r\n} b", &swc)?,
-// "<p>a b</p>",
-// "should support an empty expression (2)"
-// );
-
-// assert_eq!(
-// micromark_with_options("a {/**/} b", &swc)?,
-// "<p>a b</p>",
-// "should support a multiline comment (1)"
-// );
-
-// assert_eq!(
-// micromark_with_options("a { /*\n*/\t} b", &swc)?,
-// "<p>a b</p>",
-// "should support a multiline comment (2)"
-// );
-
-// assert_eq!(
-// micromark_with_options("a {/*b*//*c*/} d", &swc)?,
-// "<p>a d</p>",
-// "should support a multiline comment (3)"
-// );
-
-// assert_eq!(
-// micromark_with_options("a {b/*c*/} d", &swc)?,
-// "<p>a d</p>",
-// "should support a multiline comment (4)"
-// );
-
-// assert_eq!(
-// micromark_with_options("a {/*b*/c} d", &swc)?,
-// "<p>a d</p>",
-// "should support a multiline comment (4)"
-// );
-
-// // To do: errors.
-// // t.throws(
-// // () => {
-// // micromark_with_options("a {//} b", &swc);
-// // },
-// // /Could not parse expression with swc: Unexpected token/,
-// // "should crash on an incorrect line comment (1)"
-// // );
-
-// // To do: errors.
-// // t.throws(
-// // () => {
-// // micromark_with_options("a { // b } c", &swc);
-// // },
-// // /Could not parse expression with swc: Unexpected token/,
-// // "should crash on an incorrect line comment (2)"
-// // );
-
-// assert_eq!(
-// micromark_with_options("a {//\n} b", &swc)?,
-// "<p>a b</p>",
-// "should support a line comment followed by a line ending"
-// );
-
-// assert_eq!(
-// micromark_with_options("a {// b\nd} d", &swc)?,
-// "<p>a d</p>",
-// "should support a line comment followed by a line ending and an expression"
-// );
-
-// assert_eq!(
-// micromark_with_options("a {b// c\n} d", &swc)?,
-// "<p>a d</p>",
-// "should support an expression followed by a line comment and a line ending"
-// );
-
-// assert_eq!(
-// micromark_with_options("a {/*b*/ // c\n} d", &swc)?,
-// "<p>a d</p>",
-// "should support comments (1)"
-// );
-
-// assert_eq!(
-// micromark_with_options("a {b.c} d", &swc)?,
-// "<p>a d</p>",
-// "should support expression statements (1)"
-// );
-
-// assert_eq!(
-// micromark_with_options("a {1 + 1} b", &swc)?,
-// "<p>a b</p>",
-// "should support expression statements (2)"
-// );
-
-// assert_eq!(
-// micromark_with_options("a {function () {}} b", &swc)?,
-// "<p>a b</p>",
-// "should support expression statements (3)"
-// );
-
-// // To do: errors.
-// // t.throws(
-// // () => {
-// // micromark_with_options("a {var b = \"c\"} d", &swc);
-// // },
-// // /Could not parse expression with swc: Unexpected token/,
-// // "should crash on non-expressions"
-// // );
-
-// assert_eq!(
-// micromark_with_options("> a {\n> b} c", &swc)?,
-// "<blockquote>\n<p>a c</p>\n</blockquote>",
-// "should support expressions in containers"
-// );
-
-// // To do: errors.
-// // t.throws(
-// // () => {
-// // micromark_with_options("> a {\n> b<} c", &swc);
-// // },
-// // /Could not parse expression with swc: Unexpected token/,
-// // "should crash on incorrect expressions in containers (1)"
-// // );
-
-// // To do: errors.
-// // t.throws(
-// // () => {
-// // micromark_with_options("> a {\n> b\n> c} d", &swc);
-// // },
-// // /Could not parse expression with swc: Unexpected content after expression/,
-// // "should crash on incorrect expressions in containers (2)"
-// // );
-
-// Ok(())
-// }
+#[test]
+fn mdx_expression_text_gnostic_core() -> Result<(), String> {
+ let swc = Options {
+ constructs: Constructs::mdx(),
+ mdx_esm_parse: Some(Box::new(parse_esm)),
+ mdx_expression_parse: Some(Box::new(parse_expression)),
+ ..Options::default()
+ };
+
+ assert_eq!(
+ micromark_with_options("a {} b", &swc)?,
+ "<p>a b</p>",
+ "should support an empty expression (1)"
+ );
+
+ assert_eq!(
+ micromark_with_options("a { \t\r\n} b", &swc)?,
+ "<p>a b</p>",
+ "should support an empty expression (2)"
+ );
+
+ assert_eq!(
+ micromark_with_options("a {/**/} b", &swc)?,
+ "<p>a b</p>",
+ "should support a multiline comment (1)"
+ );
+
+ assert_eq!(
+ micromark_with_options("a { /*\n*/\t} b", &swc)?,
+ "<p>a b</p>",
+ "should support a multiline comment (2)"
+ );
+
+ assert_eq!(
+ micromark_with_options("a {/*b*//*c*/} d", &swc)?,
+ "<p>a d</p>",
+ "should support a multiline comment (3)"
+ );
+
+ assert_eq!(
+ micromark_with_options("a {b/*c*/} d", &swc)?,
+ "<p>a d</p>",
+ "should support a multiline comment (4)"
+ );
+
+ assert_eq!(
+ micromark_with_options("a {/*b*/c} d", &swc)?,
+ "<p>a d</p>",
+ "should support a multiline comment (4)"
+ );
+
+ assert_eq!(
+ micromark_with_options("a {//} b", &swc).err().unwrap(),
+ "1:4: Could not parse expression with swc: Unexpected eof",
+ "should crash on an incorrect line comment (1)"
+ );
+
+ assert_eq!(
+ micromark_with_options("a { // b } c", &swc).err().unwrap(),
+ "1:4: Could not parse expression with swc: Unexpected eof",
+ "should crash on an incorrect line comment (2)"
+ );
+
+ assert_eq!(
+ micromark_with_options("a {//\n} b", &swc)?,
+ "<p>a b</p>",
+ "should support a line comment followed by a line ending"
+ );
+
+ assert_eq!(
+ micromark_with_options("a {// b\nd} d", &swc)?,
+ "<p>a d</p>",
+ "should support a line comment followed by a line ending and an expression"
+ );
+
+ assert_eq!(
+ micromark_with_options("a {b// c\n} d", &swc)?,
+ "<p>a d</p>",
+ "should support an expression followed by a line comment and a line ending"
+ );
+
+ assert_eq!(
+ micromark_with_options("a {/*b*/ // c\n} d", &swc)?,
+ "<p>a d</p>",
+ "should support comments (1)"
+ );
+
+ assert_eq!(
+ micromark_with_options("a {b.c} d", &swc)?,
+ "<p>a d</p>",
+ "should support expression statements (1)"
+ );
+
+ assert_eq!(
+ micromark_with_options("a {1 + 1} b", &swc)?,
+ "<p>a b</p>",
+ "should support expression statements (2)"
+ );
+
+ assert_eq!(
+ micromark_with_options("a {function () {}} b", &swc)?,
+ "<p>a b</p>",
+ "should support expression statements (3)"
+ );
+
+ assert_eq!(
+ micromark_with_options("a {var b = \"c\"} d", &swc).err().unwrap(),
+ "1:7: Could not parse expression with swc: Unexpected token `var`. Expected this, import, async, function, [ for array literal, { for object literal, @ for decorator, function, class, null, true, false, number, bigint, string, regexp, ` for template literal, (, or an identifier",
+ "should crash on non-expressions"
+ );
+
+ assert_eq!(
+ micromark_with_options("> a {\n> b} c", &swc)?,
+ "<blockquote>\n<p>a c</p>\n</blockquote>",
+ "should support expressions in containers"
+ );
+
+ assert_eq!(
+ micromark_with_options("> a {\n> b<} c", &swc)
+ .err()
+ .unwrap(),
+ "2:8: Could not parse expression with swc: Unexpected eof",
+ "should crash on incorrect expressions in containers (1)"
+ );
+
+ assert_eq!(
+ micromark_with_options("> a {\n> b\n> c} d", &swc)
+ .err()
+ .unwrap(),
+ "3:3: Could not parse expression with swc: Unexpected content after expression",
+ "should crash on incorrect expressions in containers (2)"
+ );
+
+ Ok(())
+}
#[test]
fn mdx_expression_text_agnostic() -> Result<(), String> {
@@ -197,77 +194,74 @@ fn mdx_expression_text_agnostic() -> Result<(), String> {
Ok(())
}
-// // To do: swc.
-// #[test]
-// fn mdx_expression_text_gnostic() -> Result<(), String> {
-// assert_eq!(
-// micromark_with_options("a {b} c", &swc)?,
-// "<p>a c</p>",
-// "should support an expression"
-// );
-
-// // To do: errors.
-// // t.throws(
-// // () => {
-// // micromark_with_options("a {??} b", &swc);
-// // },
-// // /Could not parse expression with swc: Unexpected token/,
-// // "should crash on an incorrect expression"
-// // );
-
-// assert_eq!(
-// micromark_with_options("a {} b", &swc)?,
-// "<p>a b</p>",
-// "should support an empty expression"
-// );
-
-// // To do: errors.
-// // t.throws(
-// // () => {
-// // micromark_with_options("a {b c", &swc);
-// // },
-// // /Unexpected end of file in expression, expected a corresponding closing brace for `{`/,
-// // "should crash if no closing brace is found (1)"
-// // );
-
-// // To do: errors.
-// // t.throws(
-// // () => {
-// // micromark_with_options("a {b { c } d", &swc);
-// // },
-// // /Could not parse expression with swc: Unexpected content after expression/,
-// // "should crash if no closing brace is found (2)"
-// // );
-
-// assert_eq!(
-// micromark_with_options("a {\n} b", &swc)?,
-// "<p>a b</p>",
-// "should support a line ending in an expression"
-// );
-
-// assert_eq!(
-// micromark_with_options("a } b", &swc)?,
-// "<p>a } b</p>",
-// "should support just a closing brace"
-// );
-
-// assert_eq!(
-// micromark_with_options("{ a } b", &swc)?,
-// "<p> b</p>",
-// "should support expressions as the first thing when following by other things"
-// );
-
-// assert_eq!(
-// micromark_with_options("a { /* { */ } b", &swc)?,
-// "<p>a b</p>",
-// "should support an unbalanced opening brace (if JS permits)"
-// );
-
-// assert_eq!(
-// micromark_with_options("a { /* } */ } b", &swc)?,
-// "<p>a b</p>",
-// "should support an unbalanced closing brace (if JS permits)"
-// );
-
-// Ok(())
-// }
+#[test]
+fn mdx_expression_text_gnostic() -> Result<(), String> {
+ let swc = Options {
+ constructs: Constructs::mdx(),
+ mdx_esm_parse: Some(Box::new(parse_esm)),
+ mdx_expression_parse: Some(Box::new(parse_expression)),
+ ..Options::default()
+ };
+
+ assert_eq!(
+ micromark_with_options("a {b} c", &swc)?,
+ "<p>a c</p>",
+ "should support an expression"
+ );
+
+ assert_eq!(
+ micromark_with_options("a {??} b", &swc).err().unwrap(),
+ "1:9: Could not parse expression with swc: Unexpected eof",
+ "should crash on an incorrect expression"
+ );
+
+ assert_eq!(
+ micromark_with_options("a {} b", &swc)?,
+ "<p>a b</p>",
+ "should support an empty expression"
+ );
+
+ assert_eq!(
+ micromark_with_options("a {b c", &swc).err().unwrap(),
+ "1:7: Unexpected end of file in expression, expected a corresponding closing brace for `{`",
+ "should crash if no closing brace is found (1)"
+ );
+
+ assert_eq!(
+ micromark_with_options("a {b { c } d", &swc).err().unwrap(),
+ "1:6: Could not parse expression with swc: Unexpected content after expression",
+ "should crash if no closing brace is found (2)"
+ );
+
+ assert_eq!(
+ micromark_with_options("a {\n} b", &swc)?,
+ "<p>a b</p>",
+ "should support a line ending in an expression"
+ );
+
+ assert_eq!(
+ micromark_with_options("a } b", &swc)?,
+ "<p>a } b</p>",
+ "should support just a closing brace"
+ );
+
+ assert_eq!(
+ micromark_with_options("{ a } b", &swc)?,
+ "<p> b</p>",
+ "should support expressions as the first thing when following by other things"
+ );
+
+ assert_eq!(
+ micromark_with_options("a { /* { */ } b", &swc)?,
+ "<p>a b</p>",
+ "should support an unbalanced opening brace (if JS permits)"
+ );
+
+ assert_eq!(
+ micromark_with_options("a { /* } */ } b", &swc)?,
+ "<p>a b</p>",
+ "should support an unbalanced closing brace (if JS permits)"
+ );
+
+ Ok(())
+}
diff --git a/tests/mdx_jsx_text.rs b/tests/mdx_jsx_text.rs
index cf507ee..be76d6f 100644
--- a/tests/mdx_jsx_text.rs
+++ b/tests/mdx_jsx_text.rs
@@ -1,6 +1,8 @@
extern crate micromark;
+mod test_utils;
use micromark::{micromark_with_options, Constructs, Options};
use pretty_assertions::assert_eq;
+use test_utils::{parse_esm, parse_expression};
#[test]
fn mdx_jsx_text_core() -> Result<(), String> {
@@ -84,99 +86,97 @@ fn mdx_jsx_text_agnosic() -> Result<(), String> {
#[test]
fn mdx_jsx_text_gnostic() -> Result<(), String> {
- let mdx = Options {
+ let swc = Options {
constructs: Constructs::mdx(),
+ mdx_esm_parse: Some(Box::new(parse_esm)),
+ mdx_expression_parse: Some(Box::new(parse_expression)),
..Options::default()
};
assert_eq!(
- micromark_with_options("a <b /> c", &mdx)?,
+ micromark_with_options("a <b /> c", &swc)?,
"<p>a c</p>",
"should support a self-closing element"
);
assert_eq!(
- micromark_with_options("a <b> c </b> d", &mdx)?,
+ micromark_with_options("a <b> c </b> d", &swc)?,
"<p>a c d</p>",
"should support a closed element"
);
assert_eq!(
- micromark_with_options("a <b> c", &mdx)?,
+ micromark_with_options("a <b> c", &swc)?,
"<p>a c</p>",
"should support an unclosed element"
);
assert_eq!(
- micromark_with_options("a <b {...c} /> d", &mdx)?,
+ micromark_with_options("a <b {...c} /> d", &swc)?,
"<p>a d</p>",
"should support an attribute expression"
);
assert_eq!(
- micromark_with_options("a <b {...{c: 1, d: Infinity, e: false}} /> f", &mdx)?,
+ micromark_with_options("a <b {...{c: 1, d: Infinity, e: false}} /> f", &swc)?,
"<p>a f</p>",
"should support more complex attribute expression (1)"
);
assert_eq!(
- micromark_with_options("a <b {...[1, Infinity, false]} /> d", &mdx)?,
+ micromark_with_options("a <b {...[1, Infinity, false]} /> d", &swc)?,
"<p>a d</p>",
"should support more complex attribute expression (2)"
);
assert_eq!(
- micromark_with_options("a <b c={1 + 1} /> d", &mdx)?,
+ micromark_with_options("a <b c={1 + 1} /> d", &swc)?,
"<p>a d</p>",
"should support an attribute value expression"
);
assert_eq!(
- micromark_with_options("a <b c={} /> d", &mdx)
+ micromark_with_options("a <b c={} /> d", &swc)
.err()
.unwrap(),
- "1:9: Unexpected empty expression, expected a value between braces",
+ "1:15: Could not parse expression with swc: Unexpected eof",
"should crash on an empty attribute value expression"
);
- // To do: swc.
- // assert_eq!(
- // micromark_with_options("a <b {1 + 1} /> c", &swc)
- // .err()
- // .unwrap(),
- // "Could not parse expression with acorn: Unexpected token",
- // "should crash on a non-spread attribute expression"
- // );
-
- // To do: swc.
- // assert_eq!(
- // micromark_with_options("a <b c={?} /> d", &swc)
- // .err()
- // .unwrap(),
- // "Could not parse expression with acorn: Unexpected token",
- // "should crash on invalid JS in an attribute value expression"
- // );
-
- // To do: swc.
- // assert_eq!(
- // micromark_with_options("a <b {?} /> c", &swc)
- // .err()
- // .unwrap(),
- // "Could not parse expression with acorn: Unexpected token",
- // "should crash on invalid JS in an attribute expression"
- // );
-
- // To do: swc.
- // assert_eq!(
- // micromark_with_options("a <b{c=d}={}/> f", &swc)
- // .err()
- // .unwrap(),
- // "Unexpected `ExpressionStatement` in code: expected an object spread",
- // "should crash on invalid JS in an attribute expression (2)"
- // );
-
- assert_eq!(
- micromark_with_options("a <b c={(2)} d={<e />} /> f", &mdx)?,
+ assert_eq!(
+ micromark_with_options("a <b {1 + 1} /> c", &swc)
+ .err()
+ .unwrap(),
+ "1:18: Could not parse expression with swc: Expected ',', got '}'",
+ "should crash on a non-spread attribute expression"
+ );
+
+ assert_eq!(
+ micromark_with_options("a <b c={?} /> d", &swc)
+ .err()
+ .unwrap(),
+ "1:16: Could not parse expression with swc: Unexpected token `?`. Expected this, import, async, function, [ for array literal, { for object literal, @ for decorator, function, class, null, true, false, number, bigint, string, regexp, ` for template literal, (, or an identifier",
+ "should crash on invalid JS in an attribute value expression"
+ );
+
+ assert_eq!(
+ micromark_with_options("a <b {?} /> c", &swc)
+ .err()
+ .unwrap(),
+ "1:14: Could not parse expression with swc: Unexpected token `?`. Expected identifier, string literal, numeric literal or [ for the computed key",
+ "should crash on invalid JS in an attribute expression"
+ );
+
+ assert_eq!(
+ micromark_with_options("a <b{c=d}={}/> f", &swc)
+ .err()
+ .unwrap(),
+ "1:6: Expected a single spread value, such as `...x`",
+ "should crash on invalid JS in an attribute expression (2)"
+ );
+
+ assert_eq!(
+ micromark_with_options("a <b c={(2)} d={<e />} /> f", &swc)?,
"<p>a f</p>",
"should support parenthesized expressions"
);
diff --git a/tests/mdx_swc.rs b/tests/mdx_swc.rs
new file mode 100644
index 0000000..c9a2a61
--- /dev/null
+++ b/tests/mdx_swc.rs
@@ -0,0 +1,47 @@
+extern crate micromark;
+mod test_utils;
+use micromark::{micromark_with_options, Constructs, Options};
+use pretty_assertions::assert_eq;
+use test_utils::{parse_esm, parse_expression};
+
+#[test]
+fn mdx_swc() -> Result<(), String> {
+ let mdx = Options {
+ constructs: Constructs::mdx(),
+ mdx_esm_parse: Some(Box::new(parse_esm)),
+ mdx_expression_parse: Some(Box::new(parse_expression)),
+ ..Options::default()
+ };
+
+ assert_eq!(
+ micromark_with_options("{'}'}", &mdx)?,
+ "",
+ "should support JavaScript-aware flow expressions w/ `mdx_expression_parse`"
+ );
+
+ assert_eq!(
+ micromark_with_options("a {'}'} b", &mdx)?,
+ "<p>a b</p>",
+ "should support JavaScript-aware text expressions w/ `mdx_expression_parse`"
+ );
+
+ assert_eq!(
+ micromark_with_options("<a {...a/*}*/} />", &mdx)?,
+ "",
+ "should support JavaScript-aware attribute expressions w/ `mdx_expression_parse`"
+ );
+
+ assert_eq!(
+ micromark_with_options("<a b={'}'} />", &mdx)?,
+ "",
+ "should support JavaScript-aware attribute value expressions w/ `mdx_expression_parse`"
+ );
+
+ assert_eq!(
+ micromark_with_options("import a from 'b'\n\nexport {a}\n\n# c", &mdx)?,
+ "<h1>c</h1>",
+ "should support JavaScript-aware ESM w/ `mdx_esm_parse`"
+ );
+
+ Ok(())
+}
diff --git a/tests/test_utils/mod.rs b/tests/test_utils/mod.rs
new file mode 100644
index 0000000..10b9643
--- /dev/null
+++ b/tests/test_utils/mod.rs
@@ -0,0 +1,245 @@
+extern crate micromark;
+extern crate swc_common;
+extern crate swc_ecma_ast;
+extern crate swc_ecma_parser;
+use micromark::{MdxExpressionKind, MdxSignal};
+use swc_common::{source_map::Pos, BytePos, FileName, SourceFile, Spanned};
+use swc_ecma_ast::{EsVersion, Expr, Module};
+use swc_ecma_parser::{
+ error::Error as SwcError, parse_file_as_expr, parse_file_as_module, EsConfig, Syntax,
+};
+
+/// Parse ESM in MDX with SWC.
+pub fn parse_esm(value: &str) -> MdxSignal {
+ let (file, syntax, version) = create_config(value.to_string());
+ let mut errors = vec![];
+ let result = parse_file_as_module(&file, syntax, version, None, &mut errors);
+
+ match result {
+ Err(error) => swc_error_to_signal(&error, value.len(), 0, "esm"),
+ Ok(tree) => {
+ if errors.is_empty() {
+ check_esm_ast(tree)
+ } else {
+ if errors.len() > 1 {
+ println!("parse_esm: todo: multiple errors? {:?}", errors);
+ }
+ swc_error_to_signal(&errors[0], value.len(), 0, "esm")
+ }
+ }
+ }
+}
+
+/// Parse expressions in MDX with SWC.
+pub fn parse_expression(value: &str, kind: MdxExpressionKind) -> MdxSignal {
+ // Empty expressions are OK.
+ if matches!(kind, MdxExpressionKind::Expression)
+ && matches!(whitespace_and_comments(0, value), MdxSignal::Ok)
+ {
+ return MdxSignal::Ok;
+ }
+
+ // For attribute expression, a spread is needed, for which we have to prefix
+ // and suffix the input.
+ // See `check_expression_ast` for how the AST is verified.
+ let (prefix, suffix) = if matches!(kind, MdxExpressionKind::AttributeExpression) {
+ ("({", "})")
+ } else {
+ ("", "")
+ };
+
+ let (file, syntax, version) = create_config(format!("{}{}{}", prefix, value, suffix));
+ let mut errors = vec![];
+ let result = parse_file_as_expr(&file, syntax, version, None, &mut errors);
+
+ match result {
+ Err(error) => swc_error_to_signal(&error, value.len(), prefix.len(), "expression"),
+ Ok(tree) => {
+ if errors.is_empty() {
+ let place = fix_swc_position(tree.span().hi.to_usize(), prefix.len());
+ let result = check_expression_ast(tree, kind);
+ if matches!(result, MdxSignal::Ok) {
+ whitespace_and_comments(place, value)
+ } else {
+ result
+ }
+ } else {
+ if errors.len() > 1 {
+ unreachable!("parse_expression: todo: multiple errors? {:?}", errors);
+ }
+ swc_error_to_signal(&errors[0], value.len(), prefix.len(), "expression")
+ }
+ }
+ }
+}
+
+/// Check that the resulting AST of ESM is OK.
+///
+/// This checks that only module declarations (import/exports) are used, not
+/// statements.
+fn check_esm_ast(tree: Module) -> MdxSignal {
+ let mut index = 0;
+ while index < tree.body.len() {
+ let node = &tree.body[index];
+
+ if !node.is_module_decl() {
+ let place = fix_swc_position(node.span().hi.to_usize(), 0);
+ return MdxSignal::Error(
+ "Unexpected statement in code: only import/exports are supported".to_string(),
+ place,
+ );
+ }
+
+ index += 1;
+ }
+
+ MdxSignal::Ok
+}
+
+/// Check that the resulting AST of an expressions is OK.
+///
+/// This checks that attribute expressions are the expected spread.
+fn check_expression_ast(tree: Box<Expr>, kind: MdxExpressionKind) -> MdxSignal {
+ if matches!(kind, MdxExpressionKind::AttributeExpression)
+ && tree
+ .unwrap_parens()
+ .as_object()
+ .and_then(|object| {
+ if object.props.len() == 1 {
+ object.props[0].as_spread()
+ } else {
+ None
+ }
+ })
+ .is_none()
+ {
+ MdxSignal::Error(
+ "Expected a single spread value, such as `...x`".to_string(),
+ 0,
+ )
+ } else {
+ MdxSignal::Ok
+ }
+}
+
+/// Turn an SWC error into an `MdxSignal`.
+///
+/// * If the error happens at `value_len`, yields `MdxSignal::Eof`
+/// * Else, yields `MdxSignal::Error`.
+fn swc_error_to_signal(
+ error: &SwcError,
+ value_len: usize,
+ prefix_len: usize,
+ name: &str,
+) -> MdxSignal {
+ let message = error.kind().msg().to_string();
+ let place = fix_swc_position(error.span().hi.to_usize(), prefix_len);
+ let message = format!("Could not parse {} with swc: {}", name, message);
+
+ if place >= value_len {
+ MdxSignal::Eof(message)
+ } else {
+ MdxSignal::Error(message, place)
+ }
+}
+
+/// Move past JavaScript whitespace (well, actually ASCII whitespace) and
+/// comments.
+///
+/// This is needed because for expressions, we use an API that parses up to
+/// a valid expression, but there may be more expressions after it, which we
+/// don’t alow.
+fn whitespace_and_comments(mut index: usize, value: &str) -> MdxSignal {
+ let bytes = value.as_bytes();
+ let len = bytes.len();
+ let mut in_multiline = false;
+ let mut in_line = false;
+
+ while index < len {
+ // In a multiline comment: `/* a */`.
+ if in_multiline {
+ if index + 1 < len && bytes[index] == b'*' && bytes[index + 1] == b'/' {
+ index += 1;
+ in_multiline = false;
+ }
+ }
+ // In a line comment: `// a`.
+ else if in_line {
+ if index + 1 < len && bytes[index] == b'\r' && bytes[index + 1] == b'\n' {
+ index += 1;
+ in_line = false;
+ } else if bytes[index] == b'\r' || bytes[index] == b'\n' {
+ in_line = false;
+ }
+ }
+ // Not in a comment, opening a multiline comment: `/* a */`.
+ else if index + 1 < len && bytes[index] == b'/' && bytes[index + 1] == b'*' {
+ index += 1;
+ in_multiline = true;
+ }
+ // Not in a comment, opening a line comment: `// a`.
+ else if index + 1 < len && bytes[index] == b'/' && bytes[index + 1] == b'/' {
+ index += 1;
+ in_line = true;
+ }
+ // Outside comment, whitespace.
+ else if bytes[index].is_ascii_whitespace() {
+ // Fine!
+ }
+ // Outside comment, not whitespace.
+ else {
+ return MdxSignal::Error(
+ "Could not parse expression with swc: Unexpected content after expression"
+ .to_string(),
+ index,
+ );
+ }
+
+ index += 1;
+ }
+
+ if in_multiline {
+ MdxSignal::Error(
+ "Could not parse expression with swc: Unexpected unclosed multiline comment, expected closing: `*/`".to_string(),
+ index,
+ )
+ } else if in_line {
+ // EOF instead of EOL is specifically not allowed, because that would
+ // mean the closing brace is on the commented-out line
+ MdxSignal::Error(
+ "Could not parse expression with swc: Unexpected unclosed line comment, expected line ending: `\\n`".to_string(),
+ index,
+ )
+ } else {
+ MdxSignal::Ok
+ }
+}
+
+/// Create configuration for SWC, shared between ESM and expressions.
+///
+/// This enables modern JavaScript (ES2022) + JSX.
+fn create_config(source: String) -> (SourceFile, Syntax, EsVersion) {
+ (
+ // File.
+ SourceFile::new(
+ FileName::Anon,
+ false,
+ FileName::Anon,
+ source,
+ BytePos::from_usize(1),
+ ),
+ // Syntax.
+ Syntax::Es(EsConfig {
+ jsx: true,
+ ..EsConfig::default()
+ }),
+ // Version.
+ EsVersion::Es2022,
+ )
+}
+
+/// Turn an SWC byte position from a resulting AST to an offset in the original
+/// input string.
+fn fix_swc_position(index: usize, prefix_len: usize) -> usize {
+ index - 1 - prefix_len
+}