Skip to main content

molt/
lib.rs

1//! Fast, type-safe HTML generation for Rust and WebAssembly.
2//!
3//! A composite tree of plain values, escaped by default and rendered compact or pretty,
4//! compiled to native code or to `wasm32-unknown-unknown`.
5//!
6//! # Getting started
7//!
8//! ```
9//! use molt::prelude::*;
10//!
11//! let card = article()
12//!     .add_class("card p-4")
13//!     .child(h1().text("Welcome to Molt!").add_class("text-2xl"))
14//!     .child(p().text("Ultra-fast HTML generation compiled to WebAssembly."));
15//!
16//! println!("{}", card.render_pretty());
17//! ```
18//!
19//! # Escaping
20//!
21//! Text content and attribute values are escaped **when they enter the tree**, not when it
22//! is rendered, so nothing is escaped twice and nothing is missed. [`Node::Raw`] is the
23//! documented way to inject markup you already trust.
24//!
25//! URL attributes (`href`, `src`, `action`, `srcset`, … — the list is in
26//! [`core::url_policy`]) are checked **when the tree is written**: a `javascript:` or
27//! `vbscript:` URL, or `data:text/html`, renders as `#`, and so does a `data:` document in
28//! an `iframe`, `frame`, `embed` or `object`. The check also covers a value stored with
29//! [`Attribute::raw`](core::Attribute::raw), and `srcdoc` is written empty.
30//! [`Element::trusted_url`] opts out for a URL you wrote yourself. Tag and attribute names
31//! are validated too. See `SECURITY.md`.
32//!
33//! ```
34//! use molt::prelude::*;
35//! assert_eq!(p().text("<script>").render(), "<p>&lt;script&gt;</p>");
36//! ```
37//!
38//! # Render modes
39//!
40//! [`Render::render`] produces a single line; [`Render::render_pretty`] indents. Both take
41//! their configuration from a [`RenderOptions`] value rather than global state, so two
42//! threads can render the same tree differently at the same time.
43//!
44//! # Limits
45//!
46//! Rendering has no depth limit by default: the writer walks an explicit stack instead of
47//! recursing, and so do `Clone`, `PartialEq`, `Hash` and `Drop`. What deep trees do cost is
48//! output size in pretty mode, which is quadratic in depth because every line carries one
49//! indent string per level. When depth comes from untrusted input, bound it with
50//! [`RenderOptions::with_max_depth`]. See [`Render`] and `SECURITY.md`.
51//!
52//! # Golden fixtures
53//!
54//! The rendered markup of a whole page, a sitemap and a feed is compared byte for byte with
55//! files in `tests/fixtures/`, so a change to the output of valid input cannot go unnoticed.
56//! `MIGRATION.md` lists what changed between releases.
57
58#![cfg_attr(docsrs, feature(doc_cfg))]
59// The logo and favicon shown by rustdoc (docs.rs and the Pages build). The file lives at the
60// repository root and is excluded from the package, so the URL has to be absolute.
61#![doc(html_logo_url = "https://raw.githubusercontent.com/micheltlutz/molt/main/molt-logo.png")]
62#![doc(html_favicon_url = "https://raw.githubusercontent.com/micheltlutz/molt/main/molt-logo.png")]
63
64pub mod accessibility;
65pub mod core;
66pub mod document;
67pub mod elements;
68pub mod feed;
69pub mod layout;
70#[macro_use]
71pub mod macros;
72pub mod prelude;
73pub mod seo;
74pub mod sitemap;
75#[cfg(all(feature = "ssg", not(target_arch = "wasm32")))]
76#[cfg_attr(docsrs, doc(cfg(feature = "ssg")))]
77pub mod ssg;
78#[cfg(feature = "wasm")]
79#[cfg_attr(docsrs, doc(cfg(feature = "wasm")))]
80pub mod wasm;
81
82pub use crate::core::{Attribute, Element, Node, Render, RenderOptions};
83pub use crate::document::Document;
84pub use crate::layout::Layout;
85
86/// The prose guides are doc tests too, so a snippet in them cannot rot. Gated on `ssg`
87/// because both use `molt::ssg`, and on not being `wasm32`, because there the module does not
88/// exist even with the feature on (`wasm-pack test` runs the doc tests for that target).
89/// Compiled only when running doc tests.
90#[cfg(all(doctest, feature = "ssg", not(target_arch = "wasm32")))]
91mod guides {
92    #[doc = include_str!("../GETTING_STARTED.md")]
93    mod getting_started {}
94
95    #[doc = include_str!("../MIGRATION.md")]
96    mod migration {}
97}