viewpoint_js/lib.rs
1//! Compile-time validated JavaScript macro for Viewpoint.
2//!
3//! This crate provides a `js!` macro that validates JavaScript syntax at compile time,
4//! similar to how `serde_json::json!` validates JSON. This catches JavaScript syntax
5//! errors early, before they reach the browser.
6//!
7//! # Features
8//!
9//! - **Compile-time validation**: JavaScript syntax errors are caught during compilation
10//! - **Value interpolation**: Embed Rust expressions using `#{expr}` syntax (quoted/escaped)
11//! - **Raw interpolation**: Inject pre-built JavaScript using `@{expr}` syntax (unquoted)
12//! - **Zero runtime overhead**: Static strings when no interpolation is used
13//! - **Clear error messages**: Points to the exact location of syntax errors
14//!
15//! # Usage
16//!
17//! ```no_run
18//! use viewpoint_js::js;
19//! use viewpoint_js_core::ToJsValue; // Needed for value interpolation
20//!
21//! // Simple expression - produces &'static str
22//! let code = js!{ 1 + 2 };
23//!
24//! // Arrow function
25//! let code = js!{ () => window.innerWidth };
26//!
27//! // With value interpolation (requires ToJsValue in scope)
28//! let selector = ".my-class";
29//! let code = js!{ document.querySelector(#{selector}) };
30//!
31//! // With raw interpolation (inject JS expression as-is)
32//! let selector_expr = "document.querySelectorAll('.item')";
33//! let code = js!{ Array.from(@{selector_expr}) };
34//!
35//! // Multi-line function
36//! let code = js!{
37//! (() => {
38//! const items = document.querySelectorAll("li");
39//! return items.length;
40//! })()
41//! };
42//! ```
43//!
44//! # Value Interpolation (`#{expr}`)
45//!
46//! Use `#{expr}` to embed Rust values into JavaScript. Values are automatically
47//! converted to JavaScript representations via the [`ToJsValue`] trait:
48//!
49//! - Strings are quoted and escaped
50//! - Numbers are inserted as-is
51//! - Booleans become `true` or `false`
52//! - `Option::None` becomes `null`
53//!
54//! # Raw Interpolation (`@{expr}`)
55//!
56//! Use `@{expr}` to inject pre-built JavaScript expressions directly without
57//! quoting or escaping. The expression must return something that implements
58//! `AsRef<str>`. This is useful for:
59//!
60//! - Injecting dynamically-built selector expressions
61//! - Composing JavaScript from multiple parts
62//! - Including pre-validated JavaScript fragments
63//!
64//! [`ToJsValue`]: viewpoint_js_core::ToJsValue
65
66use proc_macro::TokenStream;
67
68mod interpolation;
69mod js_macro;
70mod parser;
71
72/// A macro that validates JavaScript syntax at compile time.
73///
74/// This macro accepts JavaScript code and validates its syntax during compilation.
75/// If the JavaScript is invalid, a compile-time error is produced with details
76/// about the syntax error.
77///
78/// # Output Type
79///
80/// - Without interpolation: Returns `&'static str`
81/// - With interpolation: Returns `String`
82///
83/// # Examples
84///
85/// ## Simple Expression
86///
87/// ```no_run
88/// use viewpoint_js::js;
89///
90/// let code: &str = js!{ 1 + 2 };
91/// assert_eq!(code, "1 + 2");
92/// ```
93///
94/// ## Arrow Function
95///
96/// ```no_run
97/// use viewpoint_js::js;
98///
99/// let code = js!{ () => window.innerWidth };
100/// ```
101///
102/// ## With Interpolation
103///
104/// ```no_run
105/// use viewpoint_js::js;
106/// use viewpoint_js_core::ToJsValue;
107///
108/// let selector = ".my-class";
109/// let code: String = js!{ document.querySelector(#{selector}) };
110/// ```
111///
112/// ## Invalid JavaScript (Compile Error)
113///
114/// ```compile_fail
115/// use viewpoint_js::js;
116///
117/// // This will produce a compile-time error because the JavaScript is invalid
118/// let code = js!{ function( };
119/// ```
120#[proc_macro]
121pub fn js(input: TokenStream) -> TokenStream {
122 js_macro::js_impl(input)
123}