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