askama-minify 0.3.4

A procedural macro for minifying Askama templates at compile time
Documentation

Askama Minify

简体中文

A procedural macro crate that minifies Askama templates at compile time.

Starting with 0.3.0, askama-minify no longer ships a CLI. It reads a template during compilation, minifies HTML and inline CSS/JavaScript, and injects the result as Askama's #[template(source = "...", ext = "...")].

Important: place #[template_minify(...)] above #[derive(Template)] so the macro can generate Askama's #[template(...)] attribute before the derive macro runs.

Usage

use askama::Template;
use askama_minify::template_minify;

#[template_minify(path = "index.html")]
#[derive(Template)]
struct IndexTemplate<'a> {
    title: &'a str,
}

Path resolution:

  • directories configured through Askama's [general].dirs
  • CARGO_MANIFEST_DIR/templates/<path> when no custom directories are configured
  • CARGO_MANIFEST_DIR/<path> as a compatibility fallback

This means the common Askama layout can use the short path directly:

#[template_minify(path = "index.html")]
#[derive(Template)]
struct IndexTemplate;

With the corresponding file:

templates/index.html

Inline Templates

use askama::Template;
use askama_minify::template_minify;

#[template_minify(source = "<h1>{{ title }}</h1>", ext = "html")]
#[derive(Template)]
struct InlineTemplate<'a> {
    title: &'a str,
}

When source is used, ext is required.

Forwarded Askama Arguments

path, source, and ext are handled by askama-minify. Any other arguments are forwarded to Askama's #[template(...)] attribute:

#[template_minify(path = "page.html", escape = "none")]
#[derive(Template)]
struct PageTemplate;

This expands to a minified source template:

#[template(source = "...", ext = "html", escape = "none")]

Notes

  • Template files and their include, extends, and import dependencies are tracked through include_str!, so Cargo rebuilds when any source template changes.
  • Askama config files are tracked too. Template dependencies are resolved relative to the source template, recursively minified, and passed to Askama through isolated generated files.
  • Included HTML fragments retain boundary whitespace. If a dependency graph inserts fragments into whitespace-sensitive or raw-text contexts (pre, textarea, script, style, or attributes), or contains incomplete markup whose context cannot be determined, its templates are preserved conservatively. This also protects inherited blocks and imported macros in these contexts.
  • html and htm templates are minified as HTML. JavaScript without Askama syntax is parsed before it is minified; scripts containing Askama syntax or unsupported JavaScript are preserved unchanged.
  • JavaScript and advanced CSS minification fall back to the original content when the result could introduce an HTML end tag. Askama strings, nested comments, raw blocks, quoted HTML attributes, and non-ASCII whitespace are preserved.
  • The built-in CSS minifier preserves stylesheets containing custom-property syntax unchanged because custom-property token whitespace can be significant.
  • Necessary CSS comment boundaries are retained as empty comments rather than changed into selector whitespace.
  • Template file contents and compressed results are cached within each compiler process. File metadata changes invalidate the file cache; all original dependencies remain tracked, and identical generated files are not rewritten.

Features

The default features preserve the full behavior:

  • askama-config: reads [general].dirs from askama.toml, using the lightweight basic-toml parser. Parsed config and glob results are cached within each compiler process.
  • js-minify: enables parser-backed JavaScript minification. Disable it to preserve inline JavaScript unchanged and avoid compiling the JavaScript parser.
  • advanced-css: uses lightningcss for fuller CSS minification. This is opt-in because it adds a substantially larger compiler dependency graph.

For the smallest dependency graph, disable default features. In this mode template paths use the default templates/ directory (plus the manifest-root compatibility fallback), and inline JavaScript is preserved:

askama-minify = { version = "0.3", default-features = false }

Enable advanced CSS minification with:

askama-minify = { version = "0.3", features = ["advanced-css"] }
  • CSS containing Askama syntax still falls back to the built-in minifier to avoid failed CSS parser work during compilation.
  • Non-HTML extensions are injected unchanged as Askama source templates.

Architecture

askama-minify is split into small modules around the procedural macro pipeline:

  • src/lib.rs: proc-macro entry point. It parses the attribute and target item, then delegates expansion.
  • src/args.rs: parses path, source, ext, and collects extra Askama arguments for forwarding.
  • src/item.rs: parses the target derive item and rejects an existing #[template(...)] attribute.
  • src/loader.rs: reads Askama config, recursively resolves and minifies template dependencies, and writes isolated generated templates for Askama.
  • src/expand.rs: builds the generated #[template(source = "...", ext = "...")] attribute and adds include_str! tracking for template files.
  • src/minifier.rs: public internal entry for HTML minification.
  • src/minifier/html.rs: HTML scanner that preserves Askama syntax and delegates inline <style> and <script> content.
  • src/minifier/css.rs: CSS minification. It uses the built-in conservative minifier by default and lightningcss when advanced-css is enabled.
  • src/minifier/js.rs: parser-backed JavaScript minification with a lossless fallback for Askama syntax and unsupported JavaScript.
  • src/minifier/template.rs: shared Askama block copier for {{ ... }}, {% ... %}, and {# ... #}.
  • src/minifier/util.rs: shared string trimming helpers.

The expansion flow is:

template_minify attribute
  -> parse MacroArgs
  -> parse TemplateItem
  -> load or read source template
  -> recursively resolve and minify template dependencies
  -> inject Askama #[template(source = "...", ext = "...")]
  -> emit include_str! tracking for source template files

Compile-time Benchmark

Run scripts/benchmark-compile.sh to measure cold cargo check time and peak memory for the minimal, default, and all-features dependency graphs. Each measurement uses an isolated temporary target directory and excludes dependency download time.

To measure dependency scanning separately from compiler startup and dependency builds:

cargo test --release --lib --locked benchmark_template_scanning -- --ignored --nocapture