Skip to main content

core_markdown/
configuration.rs

1// Released under the MIT License.
2// Copyright, 2022, by Bernhard Berger.
3// Copyright, 2022-2025, by Titus Wormer.
4// Copyright, 2023, by cel.
5// Copyright, 2025, by Ophir Lojkine.
6// Copyright, 2026, by Samuel Williams.
7
8use crate::util::{
9    line_ending::LineEnding,
10    mdx::{EsmParse as MdxEsmParse, ExpressionParse as MdxExpressionParse},
11};
12use alloc::{boxed::Box, fmt, string::String};
13
14/// Control which constructs are enabled.
15///
16/// Not all constructs can be configured.
17/// Notably, blank lines and paragraphs cannot be turned off.
18///
19/// ## Examples
20///
21/// ```
22/// use core_markdown::Constructs;
23/// # fn main() {
24///
25/// // Use the default trait to get `CommonMark` constructs:
26/// let commonmark = Constructs::default();
27///
28/// // To turn on all of GFM, use the `gfm` method:
29/// let gfm = Constructs::gfm();
30///
31/// // Or, mix and match:
32/// let custom = Constructs {
33///   math_flow: true,
34///   math_text: true,
35///   ..Constructs::gfm()
36/// };
37/// # }
38/// ```
39#[allow(clippy::struct_excessive_bools)]
40#[derive(Clone, Debug, Eq, PartialEq)]
41#[cfg_attr(
42    feature = "serde",
43    derive(serde::Serialize, serde::Deserialize),
44    serde(rename_all = "camelCase")
45)]
46pub struct Constructs {
47    /// Attention.
48    ///
49    /// ```markdown
50    /// > | a *b* c **d**.
51    ///       ^^^   ^^^^^
52    /// ```
53    pub attention: bool,
54    /// Autolink.
55    ///
56    /// ```markdown
57    /// > | a <https://example.com> b <user@example.org>.
58    ///       ^^^^^^^^^^^^^^^^^^^^^   ^^^^^^^^^^^^^^^^^^
59    /// ```
60    pub autolink: bool,
61    /// Block quote.
62    ///
63    /// ```markdown
64    /// > | > a
65    ///     ^^^
66    /// ```
67    pub block_quote: bool,
68    /// Character escape.
69    ///
70    /// ```markdown
71    /// > | a \* b
72    ///       ^^
73    /// ```
74    pub character_escape: bool,
75    /// Character reference.
76    ///
77    /// ```markdown
78    /// > | a &amp; b
79    ///       ^^^^^
80    /// ```
81    pub character_reference: bool,
82    /// Code (indented).
83    ///
84    /// ```markdown
85    /// > |     a
86    ///     ^^^^^
87    /// ```
88    pub code_indented: bool,
89    /// Code (fenced).
90    ///
91    /// ```markdown
92    /// > | ~~~js
93    ///     ^^^^^
94    /// > | console.log(1)
95    ///     ^^^^^^^^^^^^^^
96    /// > | ~~~
97    ///     ^^^
98    /// ```
99    pub code_fenced: bool,
100    /// Code (text).
101    ///
102    /// ```markdown
103    /// > | a `b` c
104    ///       ^^^
105    /// ```
106    pub code_text: bool,
107    /// Definition.
108    ///
109    /// ```markdown
110    /// > | [a]: b "c"
111    ///     ^^^^^^^^^^
112    /// ```
113    pub definition: bool,
114    /// Frontmatter with an unindented opening fence on the first document line,
115    /// outside list items and blockquotes, and a matching closing fence.
116    ///
117    /// All forms produce a [`Frontmatter`][crate::mdast::Frontmatter] node.
118    /// Untagged `---` and `+++` imply YAML and TOML. A format hint after either
119    /// delimiter, or a language-tagged backtick/tilde code fence, supplies an
120    /// explicit language. The parser preserves the raw body and authored info
121    /// string; HTML omits the frontmatter.
122    ///
123    /// ````markdown
124    /// > | ---
125    ///     ^^^
126    /// > | title: Neptune
127    ///     ^^^^^^^^^^^^^^
128    /// > | ---
129    ///     ^^^
130    /// ````
131    pub frontmatter: bool,
132    /// GFM: autolink literal.
133    ///
134    /// ```markdown
135    /// > | https://example.com
136    ///     ^^^^^^^^^^^^^^^^^^^
137    /// ```
138    pub gfm_autolink_literal: bool,
139    /// GFM: footnote definition.
140    ///
141    /// ```markdown
142    /// > | [^a]: b
143    ///     ^^^^^^^
144    /// ```
145    pub gfm_footnote_definition: bool,
146    /// GFM: footnote label start.
147    ///
148    /// ```markdown
149    /// > | a[^b]
150    ///      ^^
151    /// ```
152    pub gfm_label_start_footnote: bool,
153    ///
154    /// ```markdown
155    /// > | a ~b~ c.
156    ///       ^^^
157    /// ```
158    pub gfm_strikethrough: bool,
159    /// GFM: table.
160    ///
161    /// ```markdown
162    /// > | | a |
163    ///     ^^^^^
164    /// > | | - |
165    ///     ^^^^^
166    /// > | | b |
167    ///     ^^^^^
168    /// ```
169    pub gfm_table: bool,
170    /// GFM: task list item.
171    ///
172    /// ```markdown
173    /// > | * [x] y.
174    ///       ^^^
175    /// ```
176    pub gfm_task_list_item: bool,
177    /// Hard break (escape).
178    ///
179    /// ```markdown
180    /// > | a\
181    ///      ^
182    ///   | b
183    /// ```
184    pub hard_break_escape: bool,
185    /// Hard break (trailing).
186    ///
187    /// ```markdown
188    /// > | a␠␠
189    ///      ^^
190    ///   | b
191    /// ```
192    pub hard_break_trailing: bool,
193    /// Heading (atx).
194    ///
195    /// ```markdown
196    /// > | # a
197    ///     ^^^
198    /// ```
199    pub heading_atx: bool,
200    /// Heading (setext).
201    ///
202    /// ```markdown
203    /// > | a
204    ///     ^^
205    /// > | ==
206    ///     ^^
207    /// ```
208    pub heading_setext: bool,
209    /// HTML (flow).
210    ///
211    /// ```markdown
212    /// > | <div>
213    ///     ^^^^^
214    /// ```
215    pub html_flow: bool,
216    /// HTML (text).
217    ///
218    /// ```markdown
219    /// > | a <b> c
220    ///       ^^^
221    /// ```
222    pub html_text: bool,
223    /// Label start (image).
224    ///
225    /// ```markdown
226    /// > | a ![b](c) d
227    ///       ^^
228    /// ```
229    pub label_start_image: bool,
230    /// Label start (link).
231    ///
232    /// ```markdown
233    /// > | a [b](c) d
234    ///       ^
235    /// ```
236    pub label_start_link: bool,
237    /// Label end.
238    ///
239    /// ```markdown
240    /// > | a [b](c) d
241    ///         ^^^^
242    /// ```
243    pub label_end: bool,
244    /// List items.
245    ///
246    /// ```markdown
247    /// > | * a
248    ///     ^^^
249    /// ```
250    pub list_item: bool,
251    /// Math (flow).
252    ///
253    /// ```markdown
254    /// > | $$
255    ///     ^^
256    /// > | \frac{1}{2}
257    ///     ^^^^^^^^^^^
258    /// > | $$
259    ///     ^^
260    /// ```
261    pub math_flow: bool,
262    /// Math (text).
263    ///
264    /// ```markdown
265    /// > | a $b$ c
266    ///       ^^^
267    /// ```
268    pub math_text: bool,
269    /// MDX: ESM.
270    ///
271    /// ```markdown
272    /// > | import a from 'b'
273    ///     ^^^^^^^^^^^^^^^^^
274    /// ```
275    ///
276    /// > 👉 **Note**: to support ESM, you *must* pass
277    /// > [`mdx_esm_parse`][MdxEsmParse] in [`ParseOptions`][] too.
278    /// > Otherwise, ESM is treated as normal markdown.
279    pub mdx_esm: bool,
280    /// MDX: expression (flow).
281    ///
282    /// ```markdown
283    /// > | {Math.PI}
284    ///     ^^^^^^^^^
285    /// ```
286    ///
287    /// > 👉 **Note**: You *can* pass
288    /// > [`mdx_expression_parse`][MdxExpressionParse] in [`ParseOptions`][]
289    /// > too, to parse expressions according to a certain grammar (typically,
290    /// > a programming language).
291    /// > Otherwise, expressions are parsed with a basic algorithm that only
292    /// > cares about braces.
293    pub mdx_expression_flow: bool,
294    /// MDX: expression (text).
295    ///
296    /// ```markdown
297    /// > | a {Math.PI} c
298    ///       ^^^^^^^^^
299    /// ```
300    ///
301    /// > 👉 **Note**: You *can* pass
302    /// > [`mdx_expression_parse`][MdxExpressionParse] in [`ParseOptions`][]
303    /// > too, to parse expressions according to a certain grammar (typically,
304    /// > a programming language).
305    /// > Otherwise, expressions are parsed with a basic algorithm that only
306    /// > cares about braces.
307    pub mdx_expression_text: bool,
308    /// MDX: JSX (flow).
309    ///
310    /// ```markdown
311    /// > | <Component />
312    ///     ^^^^^^^^^^^^^
313    /// ```
314    ///
315    /// > 👉 **Note**: You *must* pass `html_flow: false` to use this,
316    /// > as it’s preferred when on over `mdx_jsx_flow`.
317    ///
318    /// > 👉 **Note**: You *can* pass
319    /// > [`mdx_expression_parse`][MdxExpressionParse] in [`ParseOptions`][]
320    /// > too, to parse expressions in JSX according to a certain grammar
321    /// > (typically, a programming language).
322    /// > Otherwise, expressions are parsed with a basic algorithm that only
323    /// > cares about braces.
324    pub mdx_jsx_flow: bool,
325    /// MDX: JSX (text).
326    ///
327    /// ```markdown
328    /// > | a <Component /> c
329    ///       ^^^^^^^^^^^^^
330    /// ```
331    ///
332    /// > 👉 **Note**: You *must* pass `html_text: false` to use this,
333    /// > as it’s preferred when on over `mdx_jsx_text`.
334    ///
335    /// > 👉 **Note**: You *can* pass
336    /// > [`mdx_expression_parse`][MdxExpressionParse] in [`ParseOptions`][]
337    /// > too, to parse expressions in JSX according to a certain grammar
338    /// > (typically, a programming language).
339    /// > Otherwise, expressions are parsed with a basic algorithm that only
340    /// > cares about braces.
341    pub mdx_jsx_text: bool,
342    /// Thematic break.
343    ///
344    /// ```markdown
345    /// > | ***
346    ///     ^^^
347    /// ```
348    pub thematic_break: bool,
349}
350
351impl Default for Constructs {
352    /// `CommonMark`.
353    ///
354    /// `CommonMark` is a relatively strong specification of how markdown
355    /// works.
356    /// Most markdown parsers try to follow it.
357    ///
358    /// For more information, see the `CommonMark` specification:
359    /// <https://spec.commonmark.org>.
360    fn default() -> Self {
361        Self {
362            attention: true,
363            autolink: true,
364            block_quote: true,
365            character_escape: true,
366            character_reference: true,
367            code_indented: true,
368            code_fenced: true,
369            code_text: true,
370            definition: true,
371            frontmatter: false,
372            gfm_autolink_literal: false,
373            gfm_label_start_footnote: false,
374            gfm_footnote_definition: false,
375            gfm_strikethrough: false,
376            gfm_table: false,
377            gfm_task_list_item: false,
378            hard_break_escape: true,
379            hard_break_trailing: true,
380            heading_atx: true,
381            heading_setext: true,
382            html_flow: true,
383            html_text: true,
384            label_start_image: true,
385            label_start_link: true,
386            label_end: true,
387            list_item: true,
388            math_flow: false,
389            math_text: false,
390            mdx_esm: false,
391            mdx_expression_flow: false,
392            mdx_expression_text: false,
393            mdx_jsx_flow: false,
394            mdx_jsx_text: false,
395            thematic_break: true,
396        }
397    }
398}
399
400impl Constructs {
401    /// GFM.
402    ///
403    /// GFM stands for **GitHub flavored markdown**.
404    /// GFM extends `CommonMark` and adds support for autolink literals,
405    /// footnotes, strikethrough, tables, and tasklists.
406    ///
407    /// For more information, see the GFM specification:
408    /// <https://github.github.com/gfm/>.
409    pub fn gfm() -> Self {
410        Self {
411            gfm_autolink_literal: true,
412            gfm_footnote_definition: true,
413            gfm_label_start_footnote: true,
414            gfm_strikethrough: true,
415            gfm_table: true,
416            gfm_task_list_item: true,
417            ..Self::default()
418        }
419    }
420
421    /// MDX.
422    ///
423    /// This turns on `CommonMark`, turns off some conflicting constructs
424    /// (autolinks, code (indented), and HTML), and turns on MDX (ESM,
425    /// expressions, and JSX).
426    ///
427    /// For more information, see the MDX website:
428    /// <https://mdxjs.com>.
429    ///
430    /// > 👉 **Note**: to support ESM, you *must* pass
431    /// > [`mdx_esm_parse`][MdxEsmParse] in [`ParseOptions`][] too.
432    /// > Otherwise, ESM is treated as normal markdown.
433    /// >
434    /// > You *can* pass
435    /// > [`mdx_expression_parse`][MdxExpressionParse]
436    /// > to parse expressions according to a certain grammar (typically, a
437    /// > programming language).
438    /// > Otherwise, expressions are parsed with a basic algorithm that only
439    /// > cares about braces.
440    pub fn mdx() -> Self {
441        Self {
442            autolink: false,
443            code_indented: false,
444            html_flow: false,
445            html_text: false,
446            mdx_esm: true,
447            mdx_expression_flow: true,
448            mdx_expression_text: true,
449            mdx_jsx_flow: true,
450            mdx_jsx_text: true,
451            ..Self::default()
452        }
453    }
454}
455
456/// Configuration that describes how to compile to HTML.
457///
458/// You likely either want to turn on the dangerous options
459/// (`allow_dangerous_html`, `allow_dangerous_protocol`) when dealing with
460/// input you trust, or want to customize how GFM footnotes are compiled
461/// (typically because the input markdown is not in English).
462///
463/// ## Examples
464///
465/// ```
466/// use core_markdown::CompileOptions;
467/// # fn main() {
468///
469/// // Use the default trait to get safe defaults:
470/// let safe = CompileOptions::default();
471///
472/// // Live dangerously / trust the author:
473/// let danger = CompileOptions {
474///   allow_dangerous_html: true,
475///   allow_dangerous_protocol: true,
476///   ..CompileOptions::default()
477/// };
478///
479/// // In French:
480/// let enFrançais = CompileOptions {
481///   gfm_footnote_back_label: Some("Arrière".into()),
482///   gfm_footnote_label: Some("Notes de bas de page".into()),
483///   ..CompileOptions::default()
484/// };
485/// # }
486/// ```
487#[allow(clippy::struct_excessive_bools)]
488#[derive(Clone, Debug, Default)]
489#[cfg_attr(
490    feature = "serde",
491    derive(serde::Serialize, serde::Deserialize),
492    serde(default, rename_all = "camelCase")
493)]
494pub struct CompileOptions {
495    /// Whether to allow all values in images.
496    ///
497    /// The default is `false`,
498    /// which lets `allow_dangerous_protocol` control protocol safety for
499    /// both links and images.
500    ///
501    /// Pass `true` to allow all values as `src` on images,
502    /// regardless of `allow_dangerous_protocol`.
503    /// This is safe because the
504    /// [HTML specification][whatwg-html-image-processing]
505    /// does not allow executable code in images.
506    ///
507    /// [whatwg-html-image-processing]: https://html.spec.whatwg.org/multipage/images.html#images-processing-model
508    ///
509    /// ## Examples
510    ///
511    /// ```
512    /// use core_markdown::{to_html_with_options, CompileOptions, Options};
513    /// # fn main() -> Result<(), core_markdown::message::Message> {
514    ///
515    /// // By default, some protocols in image sources are dropped:
516    /// assert_eq!(
517    ///     to_html_with_options(
518    ///         "![](data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==)",
519    ///         &Options::default()
520    ///     )?,
521    ///     "<p><img src=\"\" alt=\"\" /></p>"
522    /// );
523    ///
524    /// // Turn `allow_any_img_src` on to allow all values as `src` on images.
525    /// // This is safe because browsers do not execute code in images.
526    /// assert_eq!(
527    ///     to_html_with_options(
528    ///         "![](javascript:alert(1))",
529    ///         &Options {
530    ///             compile: CompileOptions {
531    ///               allow_any_img_src: true,
532    ///               ..CompileOptions::default()
533    ///             },
534    ///             ..Options::default()
535    ///         }
536    ///     )?,
537    ///     "<p><img src=\"javascript:alert(1)\" alt=\"\" /></p>"
538    /// );
539    /// # Ok(())
540    /// # }
541    /// ```
542    pub allow_any_img_src: bool,
543
544    /// Whether to allow (dangerous) HTML.
545    ///
546    /// The default is `false`, which still parses the HTML according to
547    /// `CommonMark` but shows the HTML as text instead of as elements.
548    ///
549    /// Pass `true` for trusted content to get actual HTML elements.
550    ///
551    /// When using GFM, make sure to also turn off `gfm_tagfilter`.
552    /// Otherwise, some dangerous HTML is still ignored.
553    ///
554    /// ## Examples
555    ///
556    /// ```
557    /// use core_markdown::{to_html, to_html_with_options, CompileOptions, Options};
558    /// # fn main() -> Result<(), core_markdown::message::Message> {
559    ///
560    /// // `markdown-rs` is safe by default:
561    /// assert_eq!(
562    ///     to_html("Hi, <i>venus</i>!"),
563    ///     "<p>Hi, &lt;i&gt;venus&lt;/i&gt;!</p>"
564    /// );
565    ///
566    /// // Turn `allow_dangerous_html` on to allow potentially dangerous HTML:
567    /// assert_eq!(
568    ///     to_html_with_options(
569    ///         "Hi, <i>venus</i>!",
570    ///         &Options {
571    ///             compile: CompileOptions {
572    ///               allow_dangerous_html: true,
573    ///               ..CompileOptions::default()
574    ///             },
575    ///             ..Options::default()
576    ///         }
577    ///     )?,
578    ///     "<p>Hi, <i>venus</i>!</p>"
579    /// );
580    /// # Ok(())
581    /// # }
582    /// ```
583    pub allow_dangerous_html: bool,
584
585    /// Whether to allow dangerous protocols in links and images.
586    ///
587    /// The default is `false`, which drops URLs in links and images that use
588    /// dangerous protocols.
589    ///
590    /// Pass `true` for trusted content to support all protocols.
591    ///
592    /// URLs that have no protocol (which means it’s relative to the current
593    /// page, such as `./some/page.html`) and URLs that have a safe protocol
594    /// (for images: `http`, `https`; for links: `http`, `https`, `irc`,
595    /// `ircs`, `mailto`, `xmpp`), are safe.
596    /// All other URLs are dangerous and dropped.
597    ///
598    /// When the option `allow_all_protocols_in_img` is enabled,
599    /// `allow_dangerous_protocol` only applies to links.
600    ///
601    /// This is safe because the
602    /// [HTML specification][whatwg-html-image-processing]
603    /// does not allow executable code in images.
604    /// All modern browsers respect this.
605    ///
606    /// [whatwg-html-image-processing]: https://html.spec.whatwg.org/multipage/images.html#images-processing-model
607    ///
608    /// ## Examples
609    ///
610    /// ```
611    /// use core_markdown::{to_html, to_html_with_options, CompileOptions, Options};
612    /// # fn main() -> Result<(), core_markdown::message::Message> {
613    ///
614    /// // `markdown-rs` is safe by default:
615    /// assert_eq!(
616    ///     to_html("<javascript:alert(1)>"),
617    ///     "<p><a href=\"\">javascript:alert(1)</a></p>"
618    /// );
619    ///
620    /// // Turn `allow_dangerous_protocol` on to allow potentially dangerous protocols:
621    /// assert_eq!(
622    ///     to_html_with_options(
623    ///         "<javascript:alert(1)>",
624    ///         &Options {
625    ///             compile: CompileOptions {
626    ///               allow_dangerous_protocol: true,
627    ///               ..CompileOptions::default()
628    ///             },
629    ///             ..Options::default()
630    ///         }
631    ///     )?,
632    ///     "<p><a href=\"javascript:alert(1)\">javascript:alert(1)</a></p>"
633    /// );
634    /// # Ok(())
635    /// # }
636    /// ```
637    pub allow_dangerous_protocol: bool,
638
639    // To do: `doc_markdown` is broken.
640    #[allow(clippy::doc_markdown)]
641    /// Default line ending to use when compiling to HTML, for line endings not
642    /// in `value`.
643    ///
644    /// Generally, `markdown-rs` copies line endings (`\r`, `\n`, `\r\n`) in
645    /// the markdown document over to the compiled HTML.
646    /// In some cases, such as `> a`, CommonMark requires that extra line
647    /// endings are added: `<blockquote>\n<p>a</p>\n</blockquote>`.
648    ///
649    /// To create that line ending, the document is checked for the first line
650    /// ending that is used.
651    /// If there is no line ending, `default_line_ending` is used.
652    /// If that isn’t configured, `\n` is used.
653    ///
654    /// ## Examples
655    ///
656    /// ```
657    /// use core_markdown::{to_html, to_html_with_options, CompileOptions, LineEnding, Options};
658    /// # fn main() -> Result<(), core_markdown::message::Message> {
659    ///
660    /// // `markdown-rs` uses `\n` by default:
661    /// assert_eq!(
662    ///     to_html("> a"),
663    ///     "<blockquote>\n<p>a</p>\n</blockquote>"
664    /// );
665    ///
666    /// // Define `default_line_ending` to configure the default:
667    /// assert_eq!(
668    ///     to_html_with_options(
669    ///         "> a",
670    ///         &Options {
671    ///             compile: CompileOptions {
672    ///               default_line_ending: LineEnding::CarriageReturnLineFeed,
673    ///               ..CompileOptions::default()
674    ///             },
675    ///             ..Options::default()
676    ///         }
677    ///     )?,
678    ///     "<blockquote>\r\n<p>a</p>\r\n</blockquote>"
679    /// );
680    /// # Ok(())
681    /// # }
682    /// ```
683    pub default_line_ending: LineEnding,
684
685    /// Textual label to describe the backreference back to footnote calls.
686    ///
687    /// The default value is `"Back to content"`.
688    /// Change it when the markdown is not in English.
689    ///
690    /// This label is used in the `aria-label` attribute on each backreference
691    /// (the `↩` links).
692    /// It affects users of assistive technology.
693    ///
694    /// ## Examples
695    ///
696    /// ```
697    /// use core_markdown::{to_html_with_options, CompileOptions, Options, ParseOptions};
698    /// # fn main() -> Result<(), core_markdown::message::Message> {
699    ///
700    /// // `"Back to content"` is used by default:
701    /// assert_eq!(
702    ///     to_html_with_options(
703    ///         "[^a]\n\n[^a]: b",
704    ///         &Options::gfm()
705    ///     )?,
706    ///     "<p><sup><a href=\"#user-content-fn-a\" id=\"user-content-fnref-a\" data-footnote-ref=\"\" aria-describedby=\"footnote-label\">1</a></sup></p>\n<section data-footnotes=\"\" class=\"footnotes\"><h2 id=\"footnote-label\" class=\"sr-only\">Footnotes</h2>\n<ol>\n<li id=\"user-content-fn-a\">\n<p>b <a href=\"#user-content-fnref-a\" data-footnote-backref=\"\" aria-label=\"Back to content\" class=\"data-footnote-backref\">↩</a></p>\n</li>\n</ol>\n</section>\n"
707    /// );
708    ///
709    /// // Pass `gfm_footnote_back_label` to use something else:
710    /// assert_eq!(
711    ///     to_html_with_options(
712    ///         "[^a]\n\n[^a]: b",
713    ///         &Options {
714    ///             parse: ParseOptions::gfm(),
715    ///             compile: CompileOptions {
716    ///               gfm_footnote_back_label: Some("Arrière".into()),
717    ///               ..CompileOptions::gfm()
718    ///             }
719    ///         }
720    ///     )?,
721    ///     "<p><sup><a href=\"#user-content-fn-a\" id=\"user-content-fnref-a\" data-footnote-ref=\"\" aria-describedby=\"footnote-label\">1</a></sup></p>\n<section data-footnotes=\"\" class=\"footnotes\"><h2 id=\"footnote-label\" class=\"sr-only\">Footnotes</h2>\n<ol>\n<li id=\"user-content-fn-a\">\n<p>b <a href=\"#user-content-fnref-a\" data-footnote-backref=\"\" aria-label=\"Arrière\" class=\"data-footnote-backref\">↩</a></p>\n</li>\n</ol>\n</section>\n"
722    /// );
723    /// # Ok(())
724    /// # }
725    /// ```
726    pub gfm_footnote_back_label: Option<String>,
727
728    /// Prefix to use before the `id` attribute on footnotes to prevent them
729    /// from *clobbering*.
730    ///
731    /// The default is `"user-content-"`.
732    /// Pass `Some("".into())` for trusted markdown and when you are careful
733    /// with polyfilling.
734    /// You could pass a different prefix.
735    ///
736    /// DOM clobbering is this:
737    ///
738    /// ```html
739    /// <p id="x"></p>
740    /// <script>alert(x) // `x` now refers to the `p#x` DOM element</script>
741    /// ```
742    ///
743    /// The above example shows that elements are made available by browsers,
744    /// by their ID, on the `window` object.
745    /// This is a security risk because you might be expecting some other
746    /// variable at that place.
747    /// It can also break polyfills.
748    /// Using a prefix solves these problems.
749    ///
750    /// ## Examples
751    ///
752    /// ```
753    /// use core_markdown::{to_html_with_options, CompileOptions, Options, ParseOptions};
754    /// # fn main() -> Result<(), core_markdown::message::Message> {
755    ///
756    /// // `"user-content-"` is used by default:
757    /// assert_eq!(
758    ///     to_html_with_options(
759    ///         "[^a]\n\n[^a]: b",
760    ///         &Options::gfm()
761    ///     )?,
762    ///     "<p><sup><a href=\"#user-content-fn-a\" id=\"user-content-fnref-a\" data-footnote-ref=\"\" aria-describedby=\"footnote-label\">1</a></sup></p>\n<section data-footnotes=\"\" class=\"footnotes\"><h2 id=\"footnote-label\" class=\"sr-only\">Footnotes</h2>\n<ol>\n<li id=\"user-content-fn-a\">\n<p>b <a href=\"#user-content-fnref-a\" data-footnote-backref=\"\" aria-label=\"Back to content\" class=\"data-footnote-backref\">↩</a></p>\n</li>\n</ol>\n</section>\n"
763    /// );
764    ///
765    /// // Pass `gfm_footnote_clobber_prefix` to use something else:
766    /// assert_eq!(
767    ///     to_html_with_options(
768    ///         "[^a]\n\n[^a]: b",
769    ///         &Options {
770    ///             parse: ParseOptions::gfm(),
771    ///             compile: CompileOptions {
772    ///               gfm_footnote_clobber_prefix: Some("".into()),
773    ///               ..CompileOptions::gfm()
774    ///             }
775    ///         }
776    ///     )?,
777    ///     "<p><sup><a href=\"#fn-a\" id=\"fnref-a\" data-footnote-ref=\"\" aria-describedby=\"footnote-label\">1</a></sup></p>\n<section data-footnotes=\"\" class=\"footnotes\"><h2 id=\"footnote-label\" class=\"sr-only\">Footnotes</h2>\n<ol>\n<li id=\"fn-a\">\n<p>b <a href=\"#fnref-a\" data-footnote-backref=\"\" aria-label=\"Back to content\" class=\"data-footnote-backref\">↩</a></p>\n</li>\n</ol>\n</section>\n"
778    /// );
779    /// # Ok(())
780    /// # }
781    /// ```
782    pub gfm_footnote_clobber_prefix: Option<String>,
783
784    /// Attributes to use on the footnote label.
785    ///
786    /// The default value is `"class=\"sr-only\""`.
787    /// Change it to show the label and add other attributes.
788    ///
789    /// This label is typically hidden visually (assuming a `sr-only` CSS class
790    /// is defined that does that), and thus affects screen readers only.
791    /// If you do have such a class, but want to show this section to everyone,
792    /// pass an empty string.
793    /// You can also add different attributes.
794    ///
795    /// > 👉 **Note**: `id="footnote-label"` is always added, because footnote
796    /// > calls use it with `aria-describedby` to provide an accessible label.
797    ///
798    /// ## Examples
799    ///
800    /// ```
801    /// use core_markdown::{to_html_with_options, CompileOptions, Options, ParseOptions};
802    /// # fn main() -> Result<(), core_markdown::message::Message> {
803    ///
804    /// // `"class=\"sr-only\""` is used by default:
805    /// assert_eq!(
806    ///     to_html_with_options(
807    ///         "[^a]\n\n[^a]: b",
808    ///         &Options::gfm()
809    ///     )?,
810    ///     "<p><sup><a href=\"#user-content-fn-a\" id=\"user-content-fnref-a\" data-footnote-ref=\"\" aria-describedby=\"footnote-label\">1</a></sup></p>\n<section data-footnotes=\"\" class=\"footnotes\"><h2 id=\"footnote-label\" class=\"sr-only\">Footnotes</h2>\n<ol>\n<li id=\"user-content-fn-a\">\n<p>b <a href=\"#user-content-fnref-a\" data-footnote-backref=\"\" aria-label=\"Back to content\" class=\"data-footnote-backref\">↩</a></p>\n</li>\n</ol>\n</section>\n"
811    /// );
812    ///
813    /// // Pass `gfm_footnote_label_attributes` to use something else:
814    /// assert_eq!(
815    ///     to_html_with_options(
816    ///         "[^a]\n\n[^a]: b",
817    ///         &Options {
818    ///             parse: ParseOptions::gfm(),
819    ///             compile: CompileOptions {
820    ///               gfm_footnote_label_attributes: Some("class=\"footnote-heading\"".into()),
821    ///               ..CompileOptions::gfm()
822    ///             }
823    ///         }
824    ///     )?,
825    ///     "<p><sup><a href=\"#user-content-fn-a\" id=\"user-content-fnref-a\" data-footnote-ref=\"\" aria-describedby=\"footnote-label\">1</a></sup></p>\n<section data-footnotes=\"\" class=\"footnotes\"><h2 id=\"footnote-label\" class=\"footnote-heading\">Footnotes</h2>\n<ol>\n<li id=\"user-content-fn-a\">\n<p>b <a href=\"#user-content-fnref-a\" data-footnote-backref=\"\" aria-label=\"Back to content\" class=\"data-footnote-backref\">↩</a></p>\n</li>\n</ol>\n</section>\n"
826    /// );
827    /// # Ok(())
828    /// # }
829    /// ```
830    pub gfm_footnote_label_attributes: Option<String>,
831
832    /// HTML tag name to use for the footnote label element.
833    ///
834    /// The default value is `"h2"`.
835    /// Change it to match your document structure.
836    ///
837    /// This label is typically hidden visually (assuming a `sr-only` CSS class
838    /// is defined that does that), and thus affects screen readers only.
839    /// If you do have such a class, but want to show this section to everyone,
840    /// pass different attributes with the `gfm_footnote_label_attributes`
841    /// option.
842    ///
843    /// ## Examples
844    ///
845    /// ```
846    /// use core_markdown::{to_html_with_options, CompileOptions, Options, ParseOptions};
847    /// # fn main() -> Result<(), core_markdown::message::Message> {
848    ///
849    /// // `"h2"` is used by default:
850    /// assert_eq!(
851    ///     to_html_with_options(
852    ///         "[^a]\n\n[^a]: b",
853    ///         &Options::gfm()
854    ///     )?,
855    ///     "<p><sup><a href=\"#user-content-fn-a\" id=\"user-content-fnref-a\" data-footnote-ref=\"\" aria-describedby=\"footnote-label\">1</a></sup></p>\n<section data-footnotes=\"\" class=\"footnotes\"><h2 id=\"footnote-label\" class=\"sr-only\">Footnotes</h2>\n<ol>\n<li id=\"user-content-fn-a\">\n<p>b <a href=\"#user-content-fnref-a\" data-footnote-backref=\"\" aria-label=\"Back to content\" class=\"data-footnote-backref\">↩</a></p>\n</li>\n</ol>\n</section>\n"
856    /// );
857    ///
858    /// // Pass `gfm_footnote_label_tag_name` to use something else:
859    /// assert_eq!(
860    ///     to_html_with_options(
861    ///         "[^a]\n\n[^a]: b",
862    ///         &Options {
863    ///             parse: ParseOptions::gfm(),
864    ///             compile: CompileOptions {
865    ///               gfm_footnote_label_tag_name: Some("h1".into()),
866    ///               ..CompileOptions::gfm()
867    ///             }
868    ///         }
869    ///     )?,
870    ///     "<p><sup><a href=\"#user-content-fn-a\" id=\"user-content-fnref-a\" data-footnote-ref=\"\" aria-describedby=\"footnote-label\">1</a></sup></p>\n<section data-footnotes=\"\" class=\"footnotes\"><h1 id=\"footnote-label\" class=\"sr-only\">Footnotes</h1>\n<ol>\n<li id=\"user-content-fn-a\">\n<p>b <a href=\"#user-content-fnref-a\" data-footnote-backref=\"\" aria-label=\"Back to content\" class=\"data-footnote-backref\">↩</a></p>\n</li>\n</ol>\n</section>\n"
871    /// );
872    /// # Ok(())
873    /// # }
874    /// ```
875    pub gfm_footnote_label_tag_name: Option<String>,
876
877    /// Textual label to use for the footnotes section.
878    ///
879    /// The default value is `"Footnotes"`.
880    /// Change it when the markdown is not in English.
881    ///
882    /// This label is typically hidden visually (assuming a `sr-only` CSS class
883    /// is defined that does that), and thus affects screen readers only.
884    /// If you do have such a class, but want to show this section to everyone,
885    /// pass different attributes with the `gfm_footnote_label_attributes`
886    /// option.
887    ///
888    /// ## Examples
889    ///
890    /// ```
891    /// use core_markdown::{to_html_with_options, CompileOptions, Options, ParseOptions};
892    /// # fn main() -> Result<(), core_markdown::message::Message> {
893    ///
894    /// // `"Footnotes"` is used by default:
895    /// assert_eq!(
896    ///     to_html_with_options(
897    ///         "[^a]\n\n[^a]: b",
898    ///         &Options::gfm()
899    ///     )?,
900    ///     "<p><sup><a href=\"#user-content-fn-a\" id=\"user-content-fnref-a\" data-footnote-ref=\"\" aria-describedby=\"footnote-label\">1</a></sup></p>\n<section data-footnotes=\"\" class=\"footnotes\"><h2 id=\"footnote-label\" class=\"sr-only\">Footnotes</h2>\n<ol>\n<li id=\"user-content-fn-a\">\n<p>b <a href=\"#user-content-fnref-a\" data-footnote-backref=\"\" aria-label=\"Back to content\" class=\"data-footnote-backref\">↩</a></p>\n</li>\n</ol>\n</section>\n"
901    /// );
902    ///
903    /// // Pass `gfm_footnote_label` to use something else:
904    /// assert_eq!(
905    ///     to_html_with_options(
906    ///         "[^a]\n\n[^a]: b",
907    ///         &Options {
908    ///             parse: ParseOptions::gfm(),
909    ///             compile: CompileOptions {
910    ///               gfm_footnote_label: Some("Notes de bas de page".into()),
911    ///               ..CompileOptions::gfm()
912    ///             }
913    ///         }
914    ///     )?,
915    ///     "<p><sup><a href=\"#user-content-fn-a\" id=\"user-content-fnref-a\" data-footnote-ref=\"\" aria-describedby=\"footnote-label\">1</a></sup></p>\n<section data-footnotes=\"\" class=\"footnotes\"><h2 id=\"footnote-label\" class=\"sr-only\">Notes de bas de page</h2>\n<ol>\n<li id=\"user-content-fn-a\">\n<p>b <a href=\"#user-content-fnref-a\" data-footnote-backref=\"\" aria-label=\"Back to content\" class=\"data-footnote-backref\">↩</a></p>\n</li>\n</ol>\n</section>\n"
916    /// );
917    /// # Ok(())
918    /// # }
919    /// ```
920    pub gfm_footnote_label: Option<String>,
921
922    /// Whether or not GFM task list html `<input>` items are enabled.
923    ///
924    /// This determines whether or not the user of the browser is able
925    /// to click and toggle generated checkbox items. The default is false.
926    ///
927    /// ## Examples
928    ///
929    /// ```
930    /// use core_markdown::{to_html_with_options, CompileOptions, Options, ParseOptions};
931    /// # fn main() -> Result<(), core_markdown::message::Message> {
932    ///
933    /// // With `gfm_task_list_item_checkable`, generated `<input type="checkbox" />`
934    /// // tags do not contain the attribute `disabled=""` and are thus toggleable by
935    /// // browser users.
936    /// assert_eq!(
937    ///     to_html_with_options(
938    ///         "* [x] y.",
939    ///         &Options {
940    ///             parse: ParseOptions::gfm(),
941    ///             compile: CompileOptions {
942    ///                 gfm_task_list_item_checkable: true,
943    ///                 ..CompileOptions::gfm()
944    ///             }
945    ///         }
946    ///     )?,
947    ///     "<ul>\n<li><input type=\"checkbox\" checked=\"\" /> y.</li>\n</ul>"
948    /// );
949    /// # Ok(())
950    /// # }
951    /// ```
952    pub gfm_task_list_item_checkable: bool,
953
954    /// Whether to support the GFM tagfilter.
955    ///
956    /// This option does nothing if `allow_dangerous_html` is not turned on.
957    /// The default is `false`, which does not apply the GFM tagfilter to HTML.
958    /// Pass `true` for output that is a bit closer to GitHub’s actual output.
959    ///
960    /// The tagfilter is kinda weird and kinda useless.
961    /// The tag filter is a naïve attempt at XSS protection.
962    /// You should use a proper HTML sanitizing algorithm instead.
963    ///
964    /// ## Examples
965    ///
966    /// ```
967    /// use core_markdown::{to_html_with_options, CompileOptions, Options, ParseOptions};
968    /// # fn main() -> Result<(), core_markdown::message::Message> {
969    ///
970    /// // With `allow_dangerous_html`, `markdown-rs` passes HTML through untouched:
971    /// assert_eq!(
972    ///     to_html_with_options(
973    ///         "<iframe>",
974    ///         &Options {
975    ///             parse: ParseOptions::gfm(),
976    ///             compile: CompileOptions {
977    ///               allow_dangerous_html: true,
978    ///               ..CompileOptions::default()
979    ///             }
980    ///         }
981    ///     )?,
982    ///     "<iframe>"
983    /// );
984    ///
985    /// // Pass `gfm_tagfilter: true` to make some of that safe:
986    /// assert_eq!(
987    ///     to_html_with_options(
988    ///         "<iframe>",
989    ///         &Options {
990    ///             parse: ParseOptions::gfm(),
991    ///             compile: CompileOptions {
992    ///               allow_dangerous_html: true,
993    ///               gfm_tagfilter: true,
994    ///               ..CompileOptions::default()
995    ///             }
996    ///         }
997    ///     )?,
998    ///     "&lt;iframe>"
999    /// );
1000    /// # Ok(())
1001    /// # }
1002    /// ```
1003    ///
1004    /// ## References
1005    ///
1006    /// * [*§ 6.1 Disallowed Raw HTML (extension)* in GFM](https://github.github.com/gfm/#disallowed-raw-html-extension-)
1007    /// * [`cmark-gfm#extensions/tagfilter.c`](https://github.com/github/cmark-gfm/blob/master/extensions/tagfilter.c)
1008    pub gfm_tagfilter: bool,
1009
1010    /// Whether to add unique `id` attributes to headings for in-page links
1011    /// and tables of contents.
1012    ///
1013    /// IDs use the same text normalization and duplicate suffixes as
1014    /// [`mdast::Headings::extract`][crate::mdast::Headings::extract].
1015    ///
1016    /// The default is `false`.
1017    pub heading_ids: bool,
1018}
1019
1020impl CompileOptions {
1021    /// GFM.
1022    ///
1023    /// GFM stands for **GitHub flavored markdown**.
1024    /// On the compilation side, GFM turns on the GFM tag filter.
1025    /// The tagfilter is useless, but it’s included here for consistency, and
1026    /// this method exists for parity to parse options.
1027    ///
1028    /// For more information, see the GFM specification:
1029    /// <https://github.github.com/gfm/>.
1030    pub fn gfm() -> Self {
1031        Self {
1032            gfm_tagfilter: true,
1033            ..Self::default()
1034        }
1035    }
1036}
1037
1038/// Configuration that describes how to parse from markdown.
1039///
1040/// You can use this:
1041///
1042/// * To control what markdown constructs are turned on and off
1043/// * To control some of those constructs
1044/// * To add support for certain programming languages when parsing MDX
1045///
1046/// In most cases, you will want to use the default trait or `gfm` method.
1047///
1048/// ## Examples
1049///
1050/// ```
1051/// use core_markdown::ParseOptions;
1052/// # fn main() {
1053///
1054/// // Use the default trait to parse markdown according to `CommonMark`:
1055/// let commonmark = ParseOptions::default();
1056///
1057/// // Use the `gfm` method to parse markdown according to GFM:
1058/// let gfm = ParseOptions::gfm();
1059/// # }
1060/// ```
1061#[allow(clippy::struct_excessive_bools)]
1062#[cfg_attr(
1063    feature = "serde",
1064    derive(serde::Serialize, serde::Deserialize),
1065    serde(default, rename_all = "camelCase")
1066)]
1067pub struct ParseOptions {
1068    // Note: when adding fields, don’t forget to add them to `fmt::Debug` below.
1069    /// Which constructs to enable and disable.
1070    ///
1071    /// The default is to follow `CommonMark`.
1072    ///
1073    /// ## Examples
1074    ///
1075    /// ```
1076    /// use core_markdown::{to_html, to_html_with_options, Constructs, Options, ParseOptions};
1077    /// # fn main() -> Result<(), core_markdown::message::Message> {
1078    ///
1079    /// // `markdown-rs` follows CommonMark by default:
1080    /// assert_eq!(
1081    ///     to_html("    indented code?"),
1082    ///     "<pre><code>indented code?\n</code></pre>"
1083    /// );
1084    ///
1085    /// // Pass `constructs` to choose what to enable and disable:
1086    /// assert_eq!(
1087    ///     to_html_with_options(
1088    ///         "    indented code?",
1089    ///         &Options {
1090    ///             parse: ParseOptions {
1091    ///               constructs: Constructs {
1092    ///                 code_indented: false,
1093    ///                 ..Constructs::default()
1094    ///               },
1095    ///               ..ParseOptions::default()
1096    ///             },
1097    ///             ..Options::default()
1098    ///         }
1099    ///     )?,
1100    ///     "<p>indented code?</p>"
1101    /// );
1102    /// # Ok(())
1103    /// # }
1104    /// ```
1105    #[cfg_attr(feature = "serde", serde(default))]
1106    pub constructs: Constructs,
1107
1108    /// Whether to parse a language prefix, such as `ruby:`, before inline code.
1109    ///
1110    /// When enabled, the prefix is removed from the surrounding text and
1111    /// stored as the inline code node's `lang` value. The default is `false`.
1112    #[cfg_attr(feature = "serde", serde(default))]
1113    pub inline_code_info: bool,
1114
1115    /// Whether indented type 6 and type 7 HTML blocks continue across blank
1116    /// lines when later content keeps a consistent indentation.
1117    ///
1118    /// The default is `false`, which follows `CommonMark`'s blank-line rule.
1119    #[cfg_attr(feature = "serde", serde(default))]
1120    pub html_block_blank_lines: bool,
1121
1122    /// Whether `:` is allowed in HTML tag names for namespace prefixes such
1123    /// as `svg:circle`.
1124    ///
1125    /// The default is `false` to preserve `CommonMark` parsing of text such as
1126    /// `<m:abc>`.
1127    #[cfg_attr(feature = "serde", serde(default))]
1128    pub html_tag_namespaces: bool,
1129
1130    /// Whether to support GFM strikethrough with a single tilde
1131    ///
1132    /// This option does nothing if `gfm_strikethrough` is not turned on in
1133    /// `constructs`.
1134    /// This option does not affect strikethrough with double tildes.
1135    ///
1136    /// The default is `true`, which follows how markdown on `github.com`
1137    /// works, as strikethrough with single tildes is supported.
1138    /// Pass `false`, to follow the GFM spec more strictly, by not allowing
1139    /// strikethrough with single tildes.
1140    ///
1141    /// ## Examples
1142    ///
1143    /// ```
1144    /// use core_markdown::{to_html_with_options, Constructs, Options, ParseOptions};
1145    /// # fn main() -> Result<(), core_markdown::message::Message> {
1146    ///
1147    /// // `markdown-rs` supports single tildes by default:
1148    /// assert_eq!(
1149    ///     to_html_with_options(
1150    ///         "~a~",
1151    ///         &Options {
1152    ///             parse: ParseOptions {
1153    ///               constructs: Constructs::gfm(),
1154    ///               ..ParseOptions::default()
1155    ///             },
1156    ///             ..Options::default()
1157    ///         }
1158    ///     )?,
1159    ///     "<p><del>a</del></p>"
1160    /// );
1161    ///
1162    /// // Pass `gfm_strikethrough_single_tilde: false` to turn that off:
1163    /// assert_eq!(
1164    ///     to_html_with_options(
1165    ///         "~a~",
1166    ///         &Options {
1167    ///             parse: ParseOptions {
1168    ///               constructs: Constructs::gfm(),
1169    ///               gfm_strikethrough_single_tilde: false,
1170    ///               ..ParseOptions::default()
1171    ///             },
1172    ///             ..Options::default()
1173    ///         }
1174    ///     )?,
1175    ///     "<p>~a~</p>"
1176    /// );
1177    /// # Ok(())
1178    /// # }
1179    /// ```
1180    #[cfg_attr(feature = "serde", serde(default))]
1181    pub gfm_strikethrough_single_tilde: bool,
1182
1183    /// Whether to support math (text) with a single dollar
1184    ///
1185    /// This option does nothing if `math_text` is not turned on in
1186    /// `constructs`.
1187    /// This option does not affect math (text) with two or more dollars.
1188    ///
1189    /// The default is `true`, which is more close to how code (text) and
1190    /// Pandoc work, as it allows math with a single dollar to form.
1191    /// However, single dollars can interfere with “normal” dollars in text.
1192    /// Pass `false`, to only allow math (text) to form when two or more
1193    /// dollars are used.
1194    /// If you pass `false`, you can still use two or more dollars for text
1195    /// math.
1196    ///
1197    /// ## Examples
1198    ///
1199    /// ```
1200    /// use core_markdown::{to_html_with_options, Constructs, Options, ParseOptions};
1201    /// # fn main() -> Result<(), core_markdown::message::Message> {
1202    ///
1203    /// // `markdown-rs` supports single dollars by default:
1204    /// assert_eq!(
1205    ///     to_html_with_options(
1206    ///         "$a$",
1207    ///         &Options {
1208    ///             parse: ParseOptions {
1209    ///               constructs: Constructs {
1210    ///                 math_text: true,
1211    ///                 ..Constructs::default()
1212    ///               },
1213    ///               ..ParseOptions::default()
1214    ///             },
1215    ///             ..Options::default()
1216    ///         }
1217    ///     )?,
1218    ///     "<p><code class=\"language-math math-inline\">a</code></p>"
1219    /// );
1220    ///
1221    /// // Pass `math_text_single_dollar: false` to turn that off:
1222    /// assert_eq!(
1223    ///     to_html_with_options(
1224    ///         "$a$",
1225    ///         &Options {
1226    ///             parse: ParseOptions {
1227    ///               constructs: Constructs {
1228    ///                 math_text: true,
1229    ///                 ..Constructs::default()
1230    ///               },
1231    ///               math_text_single_dollar: false,
1232    ///               ..ParseOptions::default()
1233    ///             },
1234    ///             ..Options::default()
1235    ///         }
1236    ///     )?,
1237    ///     "<p>$a$</p>"
1238    /// );
1239    /// # Ok(())
1240    /// # }
1241    /// ```
1242    #[cfg_attr(feature = "serde", serde(default))]
1243    pub math_text_single_dollar: bool,
1244
1245    /// Function to parse expressions with.
1246    ///
1247    /// This function can be used to add support for arbitrary programming
1248    /// languages within expressions.
1249    ///
1250    /// It only makes sense to pass this when compiling to a syntax tree
1251    /// with [`to_mdast()`][crate::to_mdast()].
1252    ///
1253    /// For an example that adds support for JavaScript with SWC, see
1254    /// `tests/test_utils/mod.rs`.
1255    #[cfg_attr(feature = "serde", serde(skip))]
1256    pub mdx_expression_parse: Option<Box<MdxExpressionParse>>,
1257
1258    /// Function to parse ESM with.
1259    ///
1260    /// This function can be used to add support for arbitrary programming
1261    /// languages within ESM blocks, however, the keywords (`export`,
1262    /// `import`) are currently hardcoded JavaScript-specific.
1263    ///
1264    /// > 👉 **Note**: please raise an issue if you’re interested in working on
1265    /// > MDX that is aware of, say, Rust, or other programming languages.
1266    ///
1267    /// It only makes sense to pass this when compiling to a syntax tree
1268    /// with [`to_mdast()`][crate::to_mdast()].
1269    ///
1270    /// For an example that adds support for JavaScript with SWC, see
1271    /// `tests/test_utils/mod.rs`.
1272    #[cfg_attr(feature = "serde", serde(skip))]
1273    pub mdx_esm_parse: Option<Box<MdxEsmParse>>,
1274    // Note: when adding fields, don’t forget to add them to `fmt::Debug` below.
1275}
1276
1277impl fmt::Debug for ParseOptions {
1278    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1279        f.debug_struct("ParseOptions")
1280            .field("constructs", &self.constructs)
1281            .field("inline_code_info", &self.inline_code_info)
1282            .field("html_block_blank_lines", &self.html_block_blank_lines)
1283            .field("html_tag_namespaces", &self.html_tag_namespaces)
1284            .field(
1285                "gfm_strikethrough_single_tilde",
1286                &self.gfm_strikethrough_single_tilde,
1287            )
1288            .field("math_text_single_dollar", &self.math_text_single_dollar)
1289            .field(
1290                "mdx_expression_parse",
1291                &self.mdx_expression_parse.as_ref().map(|_d| "[Function]"),
1292            )
1293            .field(
1294                "mdx_esm_parse",
1295                &self.mdx_esm_parse.as_ref().map(|_d| "[Function]"),
1296            )
1297            .finish()
1298    }
1299}
1300
1301impl Default for ParseOptions {
1302    /// `CommonMark` defaults.
1303    fn default() -> Self {
1304        Self {
1305            constructs: Constructs::default(),
1306            inline_code_info: false,
1307            html_block_blank_lines: false,
1308            html_tag_namespaces: false,
1309            gfm_strikethrough_single_tilde: true,
1310            math_text_single_dollar: true,
1311            mdx_expression_parse: None,
1312            mdx_esm_parse: None,
1313        }
1314    }
1315}
1316
1317impl ParseOptions {
1318    /// GFM.
1319    ///
1320    /// GFM stands for GitHub flavored markdown.
1321    /// GFM extends `CommonMark` and adds support for autolink literals,
1322    /// footnotes, strikethrough, tables, and tasklists.
1323    ///
1324    /// For more information, see the GFM specification:
1325    /// <https://github.github.com/gfm/>
1326    pub fn gfm() -> Self {
1327        Self {
1328            constructs: Constructs::gfm(),
1329            ..Self::default()
1330        }
1331    }
1332
1333    /// MDX.
1334    ///
1335    /// This turns on `CommonMark`, turns off some conflicting constructs
1336    /// (autolinks, code (indented), and HTML), and turns on MDX (ESM,
1337    /// expressions, and JSX).
1338    ///
1339    /// For more information, see the MDX website:
1340    /// <https://mdxjs.com>.
1341    ///
1342    /// > 👉 **Note**: to support ESM, you *must* pass
1343    /// > [`mdx_esm_parse`][MdxEsmParse] in [`ParseOptions`][] too.
1344    /// > Otherwise, ESM is treated as normal markdown.
1345    /// >
1346    /// > You *can* pass
1347    /// > [`mdx_expression_parse`][MdxExpressionParse]
1348    /// > to parse expressions according to a certain grammar (typically, a
1349    /// > programming language).
1350    /// > Otherwise, expressions are parsed with a basic algorithm that only
1351    /// > cares about braces.
1352    pub fn mdx() -> Self {
1353        Self {
1354            constructs: Constructs::mdx(),
1355            ..Self::default()
1356        }
1357    }
1358}
1359
1360/// Configuration that describes how to parse from markdown and compile to
1361/// HTML.
1362///
1363/// In most cases, you will want to use the default trait or `gfm` method.
1364///
1365/// ## Examples
1366///
1367/// ```
1368/// use core_markdown::Options;
1369/// # fn main() {
1370///
1371/// // Use the default trait to compile markdown to HTML according to `CommonMark`:
1372/// let commonmark = Options::default();
1373///
1374/// // Use the `gfm` method to compile markdown to HTML according to GFM:
1375/// let gfm = Options::gfm();
1376/// # }
1377/// ```
1378#[allow(clippy::struct_excessive_bools)]
1379#[derive(Debug, Default)]
1380#[cfg_attr(
1381    feature = "serde",
1382    derive(serde::Serialize, serde::Deserialize),
1383    serde(default)
1384)]
1385pub struct Options {
1386    /// Configuration that describes how to parse from markdown.
1387    pub parse: ParseOptions,
1388    /// Configuration that describes how to compile to HTML.
1389    pub compile: CompileOptions,
1390}
1391
1392impl Options {
1393    /// GFM.
1394    ///
1395    /// GFM stands for GitHub flavored markdown.
1396    /// GFM extends `CommonMark` and adds support for autolink literals,
1397    /// footnotes, strikethrough, tables, and tasklists.
1398    /// On the compilation side, GFM turns on the GFM tag filter.
1399    /// The tagfilter is useless, but it’s included here for consistency.
1400    ///
1401    /// For more information, see the GFM specification:
1402    /// <https://github.github.com/gfm/>
1403    pub fn gfm() -> Self {
1404        Self {
1405            parse: ParseOptions::gfm(),
1406            compile: CompileOptions::gfm(),
1407        }
1408    }
1409}
1410
1411#[cfg(test)]
1412#[path = "configuration/tests.rs"]
1413mod tests;