Markdown parser fork with extended syntax for personal use.
1mod test_utils;
2use markdown::{
3 mdast::{MdxjsEsm, Node, Root},
4 message, to_html_with_options, to_mdast,
5 unist::Position,
6 Constructs, Options, ParseOptions,
7};
8use pretty_assertions::assert_eq;
9use test_utils::swc::{parse_esm, parse_expression};
10
11#[test]
12fn mdx_esm() -> Result<(), message::Message> {
13 let swc = Options {
14 parse: ParseOptions {
15 constructs: Constructs::mdx(),
16 mdx_esm_parse: Some(Box::new(parse_esm)),
17 mdx_expression_parse: Some(Box::new(parse_expression)),
18 ..Default::default()
19 },
20 ..Default::default()
21 };
22
23 assert_eq!(
24 to_html_with_options("import a from 'b'\n\nc", &swc)?,
25 "<p>c</p>",
26 "should support an import"
27 );
28
29 assert_eq!(
30 to_html_with_options("export default a\n\nb", &swc)?,
31 "<p>b</p>",
32 "should support an export"
33 );
34
35 assert_eq!(
36 to_html_with_options("impossible", &swc)?,
37 "<p>impossible</p>",
38 "should not support other keywords (`impossible`)"
39 );
40
41 assert_eq!(
42 to_html_with_options("exporting", &swc)?,
43 "<p>exporting</p>",
44 "should not support other keywords (`exporting`)"
45 );
46
47 assert_eq!(
48 to_html_with_options("import.", &swc)?,
49 "<p>import.</p>",
50 "should not support a non-whitespace after the keyword"
51 );
52
53 assert_eq!(
54 to_html_with_options("import('a')", &swc)?,
55 "<p>import('a')</p>",
56 "should not support a non-whitespace after the keyword (import-as-a-function)"
57 );
58
59 assert_eq!(
60 to_html_with_options(" import a from 'b'\n export default c", &swc)?,
61 "<p>import a from 'b'\nexport default c</p>",
62 "should not support an indent"
63 );
64
65 assert_eq!(
66 to_html_with_options("- import a from 'b'\n> export default c", &swc)?,
67 "<ul>\n<li>import a from 'b'</li>\n</ul>\n<blockquote>\n<p>export default c</p>\n</blockquote>",
68 "should not support keywords in containers"
69 );
70
71 assert_eq!(
72 to_html_with_options("import a from 'b'\nexport default c", &swc)?,
73 "",
74 "should support imports and exports in the same “block”"
75 );
76
77 assert_eq!(
78 to_html_with_options("import a from 'b'\n\nexport default c", &swc)?,
79 "",
80 "should support imports and exports in separate “blocks”"
81 );
82
83 assert_eq!(
84 to_html_with_options("a\n\nimport a from 'b'\n\nb\n\nexport default c", &swc)?,
85 "<p>a</p>\n<p>b</p>\n",
86 "should support imports and exports in between other constructs"
87 );
88
89 assert_eq!(
90 to_html_with_options("a\nimport a from 'b'\n\nb\nexport default c", &swc)?,
91 "<p>a\nimport a from 'b'</p>\n<p>b\nexport default c</p>",
92 "should not support import/exports when interrupting paragraphs"
93 );
94
95 assert_eq!(
96 to_html_with_options("import a", &swc)
97 .err()
98 .unwrap()
99 .to_string(),
100 "1:9: Could not parse esm with swc: Expected ',', got '<eof>' (mdx:swc)",
101 "should crash on invalid import/exports (1)"
102 );
103
104 assert_eq!(
105 to_html_with_options("import 1/1", &swc)
106 .err()
107 .unwrap()
108 .to_string(),
109 "1:8: Could not parse esm with swc: Expected 'from', got 'numeric literal (1, 1)' (mdx:swc)",
110 "should crash on invalid import/exports (2)"
111 );
112
113 assert_eq!(
114 to_html_with_options("export {\n a\n} from 'b'\n\nc", &swc)?,
115 "<p>c</p>",
116 "should support line endings in import/exports"
117 );
118
119 assert_eq!(
120 to_html_with_options("export {\n\n a\n\n} from 'b'\n\nc", &swc)?,
121 "<p>c</p>",
122 "should support blank lines in import/exports"
123 );
124
125 assert_eq!(
126 to_html_with_options("import a from 'b'\n*md*?", &swc)
127 .err()
128 .unwrap()
129 .to_string(),
130 "2:6: Could not parse esm with swc: Expression expected (mdx:swc)",
131 "should crash on markdown after import/export w/o blank line"
132 );
133
134 assert_eq!(
135 to_html_with_options("export var a = 1\n// b\n/* c */\n\nd", &swc)?,
136 "<p>d</p>",
137 "should support comments in “blocks”"
138 );
139
140 assert_eq!(
141 to_html_with_options("export var a = 1\nvar b\n\nc", &swc)
142 .err()
143 .unwrap()
144 .to_string(),
145 "2:1: Unexpected statement in code: only import/exports are supported (mdx:swc)",
146 "should crash on other statements in “blocks”"
147 );
148
149 assert_eq!(
150 to_html_with_options("import ('a')\n\nb", &swc)
151 .err()
152 .unwrap()
153 .to_string(),
154 "1:1: Unexpected statement in code: only import/exports are supported (mdx:swc)",
155 "should crash on import-as-a-function with a space `import (x)`"
156 );
157
158 assert_eq!(
159 to_html_with_options("import a from 'b'\nexport {a}\n\nc", &swc)?,
160 "<p>c</p>",
161 "should support a reexport from another import"
162 );
163
164 assert_eq!(
165 to_html_with_options("import a from 'b';\nexport {a};\n\nc", &swc)?,
166 "<p>c</p>",
167 "should support a reexport from another import w/ semicolons"
168 );
169
170 assert_eq!(
171 to_html_with_options("import a from 'b'\nexport {a as default}\n\nc", &swc)?,
172 "<p>c</p>",
173 "should support a reexport default from another import"
174 );
175
176 assert_eq!(
177 to_html_with_options("export var a = () => <b />", &swc)?,
178 "",
179 "should support JSX by default"
180 );
181
182 assert_eq!(
183 to_html_with_options("export {a}\n", &swc)?,
184 "",
185 "should support EOF after EOL"
186 );
187
188 assert_eq!(
189 to_html_with_options("import a from 'b'\n\nexport {a}\n\nc", &swc)?,
190 "<p>c</p>",
191 "should support a reexport from another esm block (1)"
192 );
193
194 assert_eq!(
195 to_html_with_options("import a from 'b'\n\nexport {a}\n\n# c", &swc)?,
196 "<h1>c</h1>",
197 "should support a reexport from another esm block (2)"
198 );
199
200 let cases = vec![
201 ("default", "import a from \"b\""),
202 ("whole", "import * as a from \"b\""),
203 ("destructuring", "import {a} from \"b\""),
204 ("destructuring and rename", "import {a as b} from \"c\""),
205 ("default and destructuring", "import a, {b as c} from \"d\""),
206 ("default and whole", "import a, * as b from \"c\""),
207 ("side-effects", "import \"a\""),
208 ];
209
210 for case in cases {
211 assert_eq!(
212 to_html_with_options(case.1, &swc)?,
213 "",
214 "should support imports: {}",
215 case.0
216 );
217 }
218
219 let cases = vec![
220 ("var", "export var a = \"\""),
221 ("const", "export const a = \"\""),
222 ("let", "export let a = \"\""),
223 ("multiple", "export var a, b"),
224 ("multiple w/ assignment", "export var a = \"a\", b = \"b\""),
225 ("function", "export function a() {}"),
226 ("class", "export class a {}"),
227 ("destructuring", "export var {a} = {}"),
228 ("rename destructuring", "export var {a: b} = {}"),
229 ("array destructuring", "export var [a] = []"),
230 ("default", "export default a = 1"),
231 ("default function", "export default function a() {}"),
232 ("default class", "export default class a {}"),
233 ("aggregate", "export * from \"a\""),
234 ("whole reexport", "export * as a from \"b\""),
235 ("reexport destructuring", "export {a} from \"b\""),
236 (
237 "reexport destructuring w rename",
238 "export {a as b} from \"c\"",
239 ),
240 ("reexport as a default whole", "export {default} from \"b\""),
241 (
242 "reexport default and non-default",
243 "export {default as a, b} from \"c\"",
244 ),
245 ];
246
247 for case in cases {
248 assert_eq!(
249 to_html_with_options(case.1, &swc)?,
250 "",
251 "should support exports: {}",
252 case.0
253 );
254 }
255
256 assert_eq!(
257 to_mdast("import a from 'b'\nexport {a}", &swc.parse)?,
258 Node::Root(Root {
259 children: vec![Node::MdxjsEsm(MdxjsEsm {
260 value: "import a from 'b'\nexport {a}".into(),
261 position: Some(Position::new(1, 1, 0, 2, 11, 28)),
262 stops: vec![(0, 0), (17, 17), (18, 18)]
263 })],
264 position: Some(Position::new(1, 1, 0, 2, 11, 28))
265 }),
266 "should support mdx esm as `MdxjsEsm`s in mdast"
267 );
268
269 Ok(())
270}