diff --git a/components/config/src/config/markup.rs b/components/config/src/config/markup.rs index 7cd03520..b1ac7fa2 100644 --- a/components/config/src/config/markup.rs +++ b/components/config/src/config/markup.rs @@ -51,6 +51,8 @@ pub struct Markdown { /// The compiled extra themes into a theme set #[serde(skip_serializing, skip_deserializing)] // not a typo, 2 are need pub extra_theme_set: Arc>, + /// Add loading="lazy" decoding="async" to img tags. When turned on, the alt text must be plain text. Defaults to false + pub lazy_async_image: bool, } impl Markdown { @@ -204,6 +206,7 @@ impl Default for Markdown { extra_syntaxes_and_themes: vec![], extra_syntax_set: None, extra_theme_set: Arc::new(None), + lazy_async_image: false, } } } diff --git a/components/markdown/src/markdown.rs b/components/markdown/src/markdown.rs index d773a21f..a6cb3bec 100644 --- a/components/markdown/src/markdown.rs +++ b/components/markdown/src/markdown.rs @@ -252,6 +252,8 @@ pub fn markdown_to_html( let mut stop_next_end_p = false; + let lazy_async_image = context.config.markdown.lazy_async_image; + let mut opts = Options::empty(); let mut has_summary = false; opts.insert(Options::ENABLE_TABLES); @@ -387,13 +389,35 @@ pub fn markdown_to_html( events.push(Event::Html("\n".into())); } Event::Start(Tag::Image(link_type, src, title)) => { - if is_colocated_asset_link(&src) { + let link = if is_colocated_asset_link(&src) { let link = format!("{}{}", context.current_page_permalink, &*src); - events.push(Event::Start(Tag::Image(link_type, link.into(), title))); + link.into() } else { - events.push(Event::Start(Tag::Image(link_type, src, title))); - } + src + }; + + events.push(if lazy_async_image { + let mut img_before_alt: String = "\"").expect("Could events.push(if lazy_async_image { + Event::Html("\" loading=\"lazy\" decoding=\"async\" />".into()) + } else { + event + }), Event::Start(Tag::Link(link_type, link, title)) if link.is_empty() => { error = Some(Error::msg("There is a link that is missing a URL")); events.push(Event::Start(Tag::Link(link_type, "#".into(), title))); diff --git a/components/markdown/tests/img.rs b/components/markdown/tests/img.rs new file mode 100644 index 00000000..c34713a9 --- /dev/null +++ b/components/markdown/tests/img.rs @@ -0,0 +1,33 @@ +mod common; +use config::Config; + +#[test] +fn can_transform_image() { + let cases = vec![ + "![haha](https://example.com/abc.jpg)", + "![](https://example.com/abc.jpg)", + "![ha\"h>a](https://example.com/abc.jpg)", + "![__ha__*ha*](https://example.com/abc.jpg)", + "![ha[ha](https://example.com)](https://example.com/abc.jpg)", + ]; + + let body = common::render(&cases.join("\n")).unwrap().body; + insta::assert_snapshot!(body); +} + +#[test] +fn can_add_lazy_loading_and_async_decoding() { + let cases = vec![ + "![haha](https://example.com/abc.jpg)", + "![](https://example.com/abc.jpg)", + "![ha\"h>a](https://example.com/abc.jpg)", + "![__ha__*ha*](https://example.com/abc.jpg)", + "![ha[ha](https://example.com)](https://example.com/abc.jpg)", + ]; + + let mut config = Config::default_for_test(); + config.markdown.lazy_async_image = true; + + let body = common::render_with_config(&cases.join("\n"), config).unwrap().body; + insta::assert_snapshot!(body); +} diff --git a/components/markdown/tests/snapshots/img__can_add_lazy_loading_and_async_decoding.snap b/components/markdown/tests/snapshots/img__can_add_lazy_loading_and_async_decoding.snap new file mode 100644 index 00000000..ed179b40 --- /dev/null +++ b/components/markdown/tests/snapshots/img__can_add_lazy_loading_and_async_decoding.snap @@ -0,0 +1,10 @@ +--- +source: components/markdown/tests/img.rs +expression: body +--- +

haha + +ha"h>a +<strong>ha</strong><em>ha</em> +ha<a href=ha" loading="lazy" decoding="async" />

+ diff --git a/components/markdown/tests/snapshots/img__can_transform_image.snap b/components/markdown/tests/snapshots/img__can_transform_image.snap new file mode 100644 index 00000000..5ad51f58 --- /dev/null +++ b/components/markdown/tests/snapshots/img__can_transform_image.snap @@ -0,0 +1,10 @@ +--- +source: components/markdown/tests/img.rs +expression: body +--- +

haha + +ha"h>a +haha +haha

+ diff --git a/docs/content/documentation/getting-started/configuration.md b/docs/content/documentation/getting-started/configuration.md index c6e57b81..ed8d8668 100644 --- a/docs/content/documentation/getting-started/configuration.md +++ b/docs/content/documentation/getting-started/configuration.md @@ -125,6 +125,11 @@ external_links_no_referrer = false # For example, `...` into `…`, `"quote"` into `“curly”` etc smart_punctuation = false +# Whether to set decoding="async" and loading="lazy" for all images +# When turned on, the alt text must be plain text. +# For example, `![xx](...)` is ok but `![*x*x](...)` isn’t ok +lazy_async_image = false + # Configuration of the link checker. [link_checker] # Skip link checking for external URLs that start with these prefixes