maud_extensions_macros/lib.rs
1#![forbid(unsafe_code)]
2#![deny(missing_docs)]
3#![deny(rustdoc::broken_intra_doc_links)]
4// Public proc-macro surface: canonical css!/js!/Component entrypoints live here.
5//! Local CSS and JS emitters for Maud.
6//!
7//! The reset story for this branch is intentionally small:
8//! - write plain `html!`
9//! - emit local CSS with `css!`
10//! - emit local JS with `js!`
11//! - optionally opt into experimental component authoring with `Component`
12//!
13//! `maud-extensions` should feel like tiny Maud superpowers, not a framework.
14
15#[cfg(feature = "components")]
16mod component;
17mod css;
18mod internal_diagnostics;
19mod js;
20mod raw_text;
21
22use proc_macro::TokenStream;
23use syn::parse_macro_input;
24
25/// Emits a `<style>` tag or defines a named local CSS helper.
26///
27/// Supported forms:
28/// - `css! { ... }`
29/// - `css!(name, { ... })`
30///
31/// ```ignore
32/// use maud::html;
33/// use maud_extensions::{css, js};
34///
35/// fn view() -> maud::Markup {
36/// css!(responsive_css, {
37/// media!("(min-width: 48rem)", {
38/// me { padding: rem!(2); }
39/// })
40/// });
41///
42/// html! {
43/// article.card {
44/// "Hello"
45/// (css! {
46/// me { color: red; }
47/// })
48/// (responsive_css())
49/// (js! {
50/// me().class_add("ready");
51/// })
52/// }
53/// }
54/// }
55/// ```
56#[proc_macro]
57pub fn css(input: TokenStream) -> TokenStream {
58 let input = parse_macro_input!(input as css::MacroInput);
59 css::expand(input)
60}
61
62/// Emits a `<script>` tag or defines a named local JS helper.
63///
64/// Supported forms:
65/// - `js! { ... }`
66/// - `js!(once, { ... })`
67/// - `js!(name, { ... })`
68/// - `js!(name, once, { ... })`
69///
70/// ```ignore
71/// use maud::html;
72/// use maud_extensions::js;
73///
74/// fn view() -> maud::Markup {
75/// html! {
76/// div {
77/// (js!(once, {
78/// me().class_add("ready");
79/// }))
80/// }
81/// }
82/// }
83/// ```
84#[proc_macro]
85pub fn js(input: TokenStream) -> TokenStream {
86 let input = parse_macro_input!(input as js::MacroInput);
87 js::expand(input)
88}
89
90/// Experimental opt-in derive for builder-centric Maud component authoring.
91///
92/// This derive is gated behind the `components` crate feature and is only a
93/// partial surface right now. The current v1 slice is Bon-backed and supports
94/// prop builders only; slot-specific ergonomics are still under construction.
95///
96/// Generated component code references `::maud_extensions::bon`. The public
97/// runtime crate re-exports Bon so downstream users only need `maud-extensions`.
98/// Repeated slot support currently relies on Bon's experimental `overwritable`
99/// feature through that re-exported dependency.
100#[cfg(feature = "components")]
101#[proc_macro_derive(Component, attributes(mx))]
102pub fn component_derive(input: TokenStream) -> TokenStream {
103 let input = parse_macro_input!(input as component::Input);
104 component::expand(input)
105}
106
107/// Experimental component impl macro for colocated render/css/js blocks.
108#[cfg(feature = "components")]
109#[proc_macro_attribute]
110pub fn component(_attr: TokenStream, input: TokenStream) -> TokenStream {
111 let input = parse_macro_input!(input as syn::ItemImpl);
112 component::expand_impl(input)
113}