···7788**How dusty are these shelves!** And yet, new books have found themselves nestled among the ancient tomes, ready to be discovered by those brave enough to explore these cursed lands.
991010-Last summer we were very happy to introduce to you Maudit, a Rust library to generate static websites, then at version 0.1, but already quite mighty. This time we're back to talk about what we've been up to recently and what's coming this year.
1010+Last summer, we were very happy to introduce Maudit to you, a Rust library to generate static websites, then at version 0.1, but already quite mighty. This time we're back to talk about what we've been up to recently and what's coming this year.
11111212> Interested in trying out Maudit? Follow our [Quick Start](/docs/quick-start/) guide.
13131414## Image processing
15151616-[Maudit 0.4.0](https://github.com/bruits/maudit/blob/main/crates/maudit/CHANGELOG.md#040) added support for image processing, allowing you to easily resize, convert and optimize images for your website at build-time.
1616+[Maudit 0.4.0](https://github.com/bruits/maudit/blob/main/crates/maudit/CHANGELOG.md#040) added support for image processing, allowing you to easily resize, convert, and optimize images for your website at build time.
17171818```rs
1919use maud::html;
···2727 let image = ctx.assets.add_image_with_options(
2828 "path/to/image.jpg",
2929 ImageOptions {
3030- width: Some(800),
3131- height: None,
3232- format: Some(ImageFormat::Png),
3030+ width: Some(800),
3131+ height: None,
3232+ format: Some(ImageFormat::Png),
3333 },
3434 )?;
3535···42424343See [our section on image processing](https://maudit.org/docs/images/#processing-images) for more information on how to use images in Maudit.
44444545-### Placeholders generation
4545+### Placeholder generation
46464747Maudit also includes the ability to easily create low-quality image placeholders (LQIP) for your images using [ThumbHash](https://evanw.github.io/thumbhash/).
4848···66666767## Customizable Markdown rendering
68686969-[Maudit 0.5.0](https://github.com/bruits/maudit/blob/main/crates/maudit/CHANGELOG.md#050) added support for components and shortcodes in Markdown files. These features allows you to completely customize how your Markdown files are rendered and enhance them with cool new possibilities.
6969+[Maudit 0.5.0](https://github.com/bruits/maudit/blob/main/crates/maudit/CHANGELOG.md#050) added support for components and shortcodes in Markdown files. These features allow you to completely customize how your Markdown files are rendered and enhance them with new possibilities.
70707171### Shortcodes
72727373-Embedding a Youtube video typically requires one to copy this long, ugly, iframe tag and configure the different attributes to make sure it renders properly, it'd be nice to have something more friendly, a code that would be short, you will.
7373+Embedding a YouTube video typically means copying a long, ugly iframe tag and configuring several attributes to ensure proper rendering. It'd be nice to have something friendlier, a code that would be short, you will.
74747575```md
7676Here's my cool video:
···103103104104### Components
105105106106-Sometimes, you want to be able to keep writing normal, spec-compliant, Markdown, but still be able to add a bit of spice to it. For this Maudit supports components, allowing you to use custom code when rendering normal Markdown elements.
106106+Sometimes, you want to keep writing normal, spec-compliant Markdown, but still be able to add a bit of spice to it. For this, Maudit supports components, allowing you to use custom code when rendering normal Markdown elements.
107107108108For instance, you may want to add an anchor icon to every heading, without needing to use a `{{ heading }}` shortcode.
109109···113113struct CustomHeading;
114114115115impl HeadingComponent for CustomHeading {
116116- fn render_start(&self, level: u8, id: Option<&str>, _classes: &[&str]) -> String {
117117- let id_attr = id.map(|i| format!(" id=\"{}\"", i)).unwrap_or_default();
118118- let href = id.map(|i| format!("#{}", i)).unwrap_or_default();
119119- format!(
120120- "<div><a href=\"{href}\"><span aria-hidden=\"true\">{}</span></a><h{level}{id_attr}>", include_str("icons/anchor.svg")
121121- )
122122- }
116116+ fn render_start(&self, level: u8, id: Option<&str>, _classes: &[&str]) -> String {
117117+ let id_attr = id.map(|i| format!(" id=\"{}\"", i)).unwrap_or_default();
118118+ let href = id.map(|i| format!("#{}", i)).unwrap_or_default();
119119+ format!(
120120+ "<div><a href=\"{href}\"><span aria-hidden=\"true\">{}</span></a><h{level}{id_attr}>", include_str("icons/anchor.svg")
121121+ )
122122+ }
123123124124- fn render_end(&self, level: u8) -> String {
125125- format!("</h{level}></div>")
126126- }
124124+ fn render_end(&self, level: u8) -> String {
125125+ format!("</h{level}></div>")
126126+ }
127127}
128128```
129129130130```rs
131131content_sources![
132132- "blog" => glob_markdown_with_options::<BlogPost>("content/blog/**/*.md", MarkdownOptions {
133133- components: MarkdownComponents::new().heading(CustomHeading),
134134- ..Default::default()
135135- }),
132132+ "blog" => glob_markdown_with_options::<BlogPost>("content/blog/**/*.md", MarkdownOptions {
133133+ components: MarkdownComponents::new().heading(CustomHeading),
134134+ ..Default::default()
135135+ }),
136136],
137137```
138138···140140141141## Improved error handling
142142143143-[Maudit 0.6.0](https://github.com/bruits/maudit/releases/tag/maudit-v0.6.0) and [0.6.6](https://github.com/bruits/maudit/releases/tag/maudit-v0.6.6) made it much easier to handle errors inside of pages by making all of the assets (which are quite prone to errors, filesystem and all) methods return Result instead of panicking.
143143+[Maudit 0.6.0](https://github.com/bruits/maudit/releases/tag/maudit-v0.6.0) and [0.6.6](https://github.com/bruits/maudit/releases/tag/maudit-v0.6.6) made it much easier to handle errors within pages by making all asset methods (which are prone to filesystem errors) return `Result` instead of panicking.
144144145145-Additionally, pages themselves can now optionally return `Result` and will bubble up their errors up the chain up to [the entrypoint](/docs/entrypoint/) when using the `?` operator. Maudit implements `Into<RenderResult>` for `Result<T: Into<RenderResult>, E: Error>`, as such using `?` and returning `Result` require no signature changes inside your pages.
145145+Additionally, pages themselves can now optionally return `Result` and will bubble their errors up the chain to [the entrypoint](/docs/entrypoint/) when using the `?` operator. Maudit implements `Into<RenderResult>` for `Result<T: Into<RenderResult>, E: Error>`, and as such, using `?` and returning `Result` requires no signature changes inside your pages.
146146147147```rs
148148use maudit::route::prelude::*;
···152152pub struct Example;
153153154154impl Route for Example {
155155- fn render(&self, ctx: &mut PageContext) -> impl Into<RenderResult> {
156156- // Use the ? operator to bubble up asset-related errors
157157- let logo = ctx.assets.add_image("images/logo.png")?;
155155+ fn render(&self, ctx: &mut PageContext) -> impl Into<RenderResult> {
156156+ // Use the ? operator to bubble up asset-related errors
157157+ let logo = ctx.assets.add_image("images/logo.png")?;
158158159159- // Wrap your return value with Ok()
160160- Ok(html! {
161161- (logo)
162162- p { "My cool logo!" }
163163- })
164164- }
159159+ // Wrap your return value with Ok()
160160+ Ok(html! {
161161+ (logo)
162162+ p { "My cool logo!" }
163163+ })
164164+ }
165165}
166166```
167167168168-Or, you can just `unwrap()` everything, that's ok! Check our section on [handling errors](/docs/routing/#handling-errors) if you'd like to learn more.
168168+Or you can just `unwrap()` everything, that's ok! Check our section on [handling errors](/docs/routing/#handling-errors) if you'd like to learn more.
169169170170## Support for internationalization
171171172172-[Maudit 0.7.0](https://github.com/bruits/maudit/releases/tag/maudit-v0.7.0) added support for internationalizating routes. For instance, you may want to have a `/about` in English, but `/a-propos` and `/om-oss` in French and Swedish respectively.
172172+[Maudit 0.7.0](https://github.com/bruits/maudit/releases/tag/maudit-v0.7.0) added support for internationalizing routes. For instance, you may want to have `/about` in English, but `/a-propos` and `/om-oss` in French and Swedish respectively.
173173174174-This is possible to do right now in Maudit: You can duplicate your `About` struct twice, register the two new routes, rewrite the `render` implementation twice.. but that's a bit cumbersome, so Maudit now allows you to generate all these pages using a single struct:
174174+This is already possible in Maudit: You can duplicate your `About` struct twice, register the two new routes, rewrite the `render` implementation twice… but that's a bit cumbersome, so Maudit now allows you to generate all these pages using a single struct:
175175176176```rust
177177use maudit::route::prelude::*;
···183183pub struct Contact;
184184185185impl Route for Contact {
186186- fn render(&self, ctx: &mut PageContext) -> impl Into<RenderResult> {
187187- match &ctx.variant {
188188- Some(language) => match language.as_str() {
189189- "sv" => "Kontakta oss.",
190190- "de" => "Kontaktieren Sie uns.",
191191- _ => unreachable!(),
192192- },
193193- _ => "Contact us.",
194194- }
186186+ fn render(&self, ctx: &mut PageContext) -> impl Into<RenderResult> {
187187+ match &ctx.variant {
188188+ Some(language) => match language.as_str() {
189189+ "sv" => "Kontakta oss.",
190190+ "de" => "Kontaktieren Sie uns.",
191191+ _ => unreachable!(),
192192+ },
193193+ _ => "Contact us.",
195194 }
195195+ }
196196}
197197```
198198199199-The ergonomics are still a bit iffy, but this nonetheless already makes it much easier to localize your website. To learn more about internationalization [visit our documentation](/docs/routing/#internationalization-i18n).
199199+The ergonomics are still a bit iffy, but this nonetheless already makes it much easier to localize your website. To learn more about internationalization, [visit our documentation](/docs/routing/#internationalization-i18n).
200200201201## Built-in sitemap generation
202202···208208use maudit::{BuildOptions, SitemapOptions, content_sources, coronate, routes};
209209210210fn main() {
211211- coronate(
212212- routes![],
213213- content_sources![],
214214- BuildOptions {
215215- base_url: Some("https://example.com".into()),
216216- sitemap: SitemapOptions {
217217- enabled: true,
218218- ..Default::default()
219219- },
220220- ..Default::default()
221221- },
222222- );
211211+ coronate(
212212+ routes![],
213213+ content_sources![],
214214+ BuildOptions {
215215+ base_url: Some("https://example.com".into()),
216216+ sitemap: SitemapOptions {
217217+ enabled: true,
218218+ ..Default::default()
219219+ },
220220+ ..Default::default()
221221+ },
222222+ );
223223}
224224```
225225226226-With this, building your website will now result in a `sitemap.xml` file being generated inside your `dist` folder which includes all the pages of your website. Maudit will also automatically handle separating your sitemap in multiple files if you have over the recommended amount of maximum 50000 pages per sitemap.
226226+With this, building your website will now result in a `sitemap.xml` file being generated inside your `dist` folder, which includes all the pages of your website. Maudit will also automatically handle separating your sitemap into multiple files if you have over the recommended maximum of 50,000 pages per sitemap.
227227228228-For more information on sitemap generation in Maudit, check [our Sitemap documentation](/docs/sitemap/).
228228+For more information on sitemap generation in Maudit, check [our sitemap documentation](/docs/sitemap/).
229229230230## Automatic prefetching
231231232232-A common complaint about MPAs (Multi-Page Applications) is that navigating between page is slow, especially compared to the app-like experience of SPAs (Single-Page Applications).
232232+A common complaint about MPAs (Multi-Page Applications) is that navigating between pages is slow, especially compared to the app-like experience of SPAs (Single-Page Applications).
233233234234-The solution to this problem is to prefetch pages before the user navigate to them, like SPAs typically do, allowing near-instant navigations in most cases.
234234+The solution to this problem is to prefetch pages before the user navigates to them, like SPAs typically do, allowing near-instant navigations in most cases.
235235236236-Since [Maudit 0.10.0](https://github.com/bruits/maudit/releases/tag/maudit-v0.10.0), Maudit will by default prefetch links on clickdown, improving page loads by around 80ms on average, with other prefetching strategies available such as prefetching on hover.
236236+Since [Maudit 0.10.0](https://github.com/bruits/maudit/releases/tag/maudit-v0.10.0), Maudit will by default prefetch links on mousedown, improving page loads by around 80 ms on average, with other prefetching strategies available such as prefetching on hover.
237237238238<video autoplay controls loop>
239239 <source src="/prefetch.mp4" type="video/mp4">
···253253pub struct Redirect;
254254255255impl Route for Redirect {
256256- fn render(&self, ctx: &mut PageContext) -> impl Into<RenderResult> {
257257- redirect("https://example.com")
256256+ fn render(&self, ctx: &mut PageContext) -> impl Into<RenderResult> {
257257+ redirect("https://example.com")
258258259259- // Use a page's url method to generate type safe links:
260260- // redirect(&OtherPage.url(None))
261261- }
259259+ // Use a page's url method to generate type-safe links:
260260+ // redirect(&OtherPage.url(None))
261261+ }
262262}
263263```
264264265265-Simple enough. The return value of this function can be directly used in your pages, making it nice and easy to redirect to new content. To learn more about internationalization [redirect yourself to our documentation](/docs/routing/#redirects).
265265+Simple enough. The return value of this function can be directly used in your pages, making it nice and easy to redirect to new content. To learn more about redirects, [redirect yourself to our documentation](/docs/routing/#redirects).
266266267267## The future
268268269269-Maudit is mightier than before, but there's still so many twisted paths we'd like to follow. Including, but not limited to:
269269+Maudit is mightier than before, but there are still so many twisted paths we'd like to follow. Including, but not limited to:
270270271271- Ability to generate variants of pages, outside of the localization system.
272272-- Support for generating PWAs automatically
273273-- Built-in font support (w/ subsetting)
272272+- Support for generating PWAs automatically.
273273+- Built-in font support (w/ subsetting).
274274- ... and more!
275275276276For now, we go back into hiding. See you soon!