oxc_markdown_parser/options.rs
1//! Parser configuration.
2//!
3//! MD and MDX are "the same syntax with different meanings":
4//! the syntactic difference reduces to which constructs are enabled,
5//! so both dialects share one parser, one AST, and one API.
6//! Downstream code never branches on a dialect enum, only on which nodes actually appear.
7
8/// Which syntax constructs the parser recognizes.
9///
10/// CommonMark's core (paragraphs, headings, thematic breaks, blockquotes,
11/// lists, fenced code, emphasis, links, hard breaks) is always on.
12/// Everything listed here is a construct that some dialect turns off or bolts on.
13///
14/// The default set mirrors Prettier's parser composition:
15/// GFM + math + wiki-link + liquid,
16/// plus container directives (`:::`), which Prettier lacks and consequently corrupts.
17#[derive(Clone, Copy, Debug, PartialEq, Eq)]
18pub struct Constructs {
19 /// `<https://example.com>` / `<user@example.com>`. Syntax error in MDX.
20 pub autolink: bool,
21 /// 4-space indented code blocks.
22 /// In MDX the indent is an ordinary paragraph (continuation) instead.
23 pub code_indented: bool,
24 /// HTML blocks (CommonMark types 1–7).
25 /// In MDX `<` at flow start is JSX.
26 pub html_flow: bool,
27 /// Inline HTML tags/comments.
28 /// In MDX `<` in text is JSX.
29 pub html_text: bool,
30
31 /// GFM bare-URL links (`www.example.com`).
32 /// Printed verbatim.
33 pub gfm_autolink_literal: bool,
34 /// GFM footnotes (`[^label]`).
35 pub gfm_footnote: bool,
36 /// GFM strikethrough (`~~x~~`).
37 pub gfm_strikethrough: bool,
38 /// Whether a single `~` also opens strikethrough.
39 /// GitHub and Prettier (`singleTilde: false`) require `~~`;
40 /// keep this off to leave subscript conventions like `H~2~O` alone.
41 pub gfm_strikethrough_single_tilde: bool,
42 /// GFM tables.
43 pub gfm_table: bool,
44 /// GFM task list items (`- [x]`).
45 pub gfm_task_list_item: bool,
46
47 /// `$$ … $$` display math.
48 pub math_flow: bool,
49 /// `$$ … $$` inline math, printed verbatim.
50 pub math_text: bool,
51 /// Whether a single `$` also opens inline math.
52 /// Prettier (`singleDollarTextMath: false`) requires `$$`;
53 /// keep this off to leave prose like `$FOO` and `$10` alone.
54 pub math_text_single_dollar: bool,
55 /// `{% … %}` / `{{ … }}` template tags, multi-line capable.
56 /// Never interpreted, kept lossless.
57 pub liquid: bool,
58 /// `[[target]]` wiki links, alias-less minimal form, printed verbatim.
59 pub wiki_link: bool,
60
61 /// `::: … :::` container directives.
62 /// Fence lines are kept verbatim (dialects disagree on their grammar);
63 /// only children are markdown.
64 pub container_directive: bool,
65
66 /// MDX `import` / `export` statements at flow start.
67 pub mdx_esm: bool,
68 /// MDX `{expr}` as its own flow block.
69 pub mdx_expression_flow: bool,
70 /// MDX `{expr}` in text.
71 pub mdx_expression_text: bool,
72 /// MDX JSX elements at flow start.
73 pub mdx_jsx_flow: bool,
74 /// MDX JSX elements in text.
75 pub mdx_jsx_text: bool,
76}
77
78impl Constructs {
79 /// Markdown mode: CommonMark + GFM + Prettier's extensions + directives.
80 pub fn markdown() -> Self {
81 Self {
82 autolink: true,
83 code_indented: true,
84 html_flow: true,
85 html_text: true,
86 gfm_autolink_literal: true,
87 gfm_footnote: true,
88 gfm_strikethrough: true,
89 gfm_strikethrough_single_tilde: false,
90 gfm_table: true,
91 gfm_task_list_item: true,
92 math_flow: true,
93 math_text: true,
94 math_text_single_dollar: false,
95 liquid: true,
96 wiki_link: true,
97 container_directive: true,
98 mdx_esm: false,
99 mdx_expression_flow: false,
100 mdx_expression_text: false,
101 mdx_jsx_flow: false,
102 mdx_jsx_text: false,
103 }
104 }
105
106 /// MDX mode: `autolink` / `code_indented` / `html_flow` / `html_text` off, the `mdx_*` family on.
107 /// Unlike markdown mode, parsing can emit diagnostics (MDX has syntax errors; CommonMark does not).
108 ///
109 /// TODO: Not usable yet: the `mdx_*` constructs are unimplemented,
110 /// and disabling `code_indented` must also lift micromark's 4-space/3-column indent limits engine-wide (currently hardcoded),
111 /// so this preset misparses e.g. ` # hi`.
112 ///
113 /// NOTE: Design:
114 /// - Expression boundaries need JavaScript lexing (`{"}"}`, `{/* } */}`, template literals),
115 /// so this preset lives behind an `mdx` cargo feature that adds `oxc_parser` as an optional dependency.
116 /// No injection point: the scanner is this crate's own module, tested here.
117 /// The oxc workspace patches `oxc_parser` to its path crate, as it does `oxc_allocator`.
118 /// - Semantics follow Prettier's MDX v3 parser (prettier/prettier#18533, micromark-extension-mdx-*):
119 /// - `{expr}` (flow, text, JSX attribute values): at each candidate `}`,
120 /// the text so far must be a complete expression (`Parser::parse_expression`, which rejects trailing content);
121 /// otherwise continue to the next `}`, and fail at EOF.
122 /// - `import` / `export`: ends at a blank line unless the parse error is at the end of input
123 /// (label start == length, or an `Unterminated ...` lexer error), then continue past it.
124 /// Prettier's `dummyAcorn` does not validate the body, so neither do we.
125 /// - JSX tags are lexed here; only their `{...}` attribute values consult the scanner.
126 /// - A hand-written brace scanner would agree on every input Prettier can format
127 /// (they differ only inside invalid JS, where Prettier throws),
128 /// which is why `oxc_lexer`, once published, can replace the probing without changing fixtures.
129 pub fn mdx() -> Self {
130 Self {
131 autolink: false,
132 code_indented: false,
133 html_flow: false,
134 html_text: false,
135 mdx_esm: true,
136 mdx_expression_flow: true,
137 mdx_expression_text: true,
138 mdx_jsx_flow: true,
139 mdx_jsx_text: true,
140 ..Self::markdown()
141 }
142 }
143}
144
145impl Default for Constructs {
146 fn default() -> Self {
147 Self::markdown()
148 }
149}
150
151/// Options for [`Parser`](crate::Parser).
152#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
153pub struct ParserOptions {
154 pub constructs: Constructs,
155}
156
157impl ParserOptions {
158 /// Markdown mode (the default): [`Constructs::markdown`].
159 pub fn markdown() -> Self {
160 Self { constructs: Constructs::markdown() }
161 }
162
163 /// MDX mode: [`Constructs::mdx`].
164 pub fn mdx() -> Self {
165 Self { constructs: Constructs::mdx() }
166 }
167}