Expand description
§Viewpoint JS - Compile-Time JavaScript Validation
This crate provides the js! macro for compile-time JavaScript validation,
catching syntax errors before they reach the browser. Similar to how
serde_json::json! validates JSON at compile time.
§Features
- Compile-time validation: JavaScript syntax errors are caught during compilation
- Value interpolation: Embed Rust values using
#{expr}syntax (quoted/escaped) - Raw interpolation: Inject pre-built JavaScript using
@{expr}syntax (unquoted) - Zero runtime overhead: Static strings when no interpolation is used
- Clear error messages: Points to the exact location of syntax errors
- Full JavaScript syntax: Single-quoted strings, template literals, regex, XPath, and more
§Quick Start
use viewpoint_js::js;
use viewpoint_js_core::ToJsValue;
// Simple expression - produces &'static str
let code = js!{ 1 + 2 };
assert_eq!(code, "1 + 2");
// Arrow function
let code = js!{ () => window.innerWidth };
// With value interpolation (requires ToJsValue in scope)
let selector = ".my-class";
let code = js!{ document.querySelector(#{selector}) };
// Multi-line function
let code = js!{
(() => {
const items = document.querySelectorAll("li");
return items.length;
})()
};§Value Interpolation (#{expr})
Use #{expr} to embed Rust values into JavaScript. Values are automatically
converted to JavaScript representations via the ToJsValue trait:
- Strings are quoted and escaped
- Numbers are inserted as-is
- Booleans become
trueorfalse Option::Nonebecomesnull
use viewpoint_js::js;
use viewpoint_js_core::ToJsValue;
let name = "John";
let age = 25;
let active = true;
let optional: Option<i32> = None;
// Strings are quoted: document.querySelector("John")
let code = js!{ document.querySelector(#{name}) };
// Numbers as-is: console.log(25)
let code = js!{ console.log(#{age}) };
// Booleans: element.disabled = true
let code = js!{ element.disabled = #{active} };
// None becomes null: setConfig(null)
let code = js!{ setConfig(#{optional}) };§Raw Interpolation (@{expr})
Use @{expr} to inject pre-built JavaScript expressions directly without
quoting or escaping. The expression must return something that implements
AsRef<str>. This is useful for:
- Injecting dynamically-built selector expressions
- Composing JavaScript from multiple parts
- Including pre-validated JavaScript fragments
use viewpoint_js::js;
// Build a selector dynamically
let selector_expr = "'.item-' + index";
let code = js!{ document.querySelector(@{selector_expr}) };
// Produces: document.querySelector('.item-' + index)
// Compose JavaScript fragments
let function_call = "myFunction()";
let code = js!{ const result = @{function_call} };
// Produces: const result = myFunction()§Output Type
- Without interpolation: Returns
&'static str(zero runtime cost) - With interpolation: Returns
String(runtime string building)
use viewpoint_js::js;
use viewpoint_js_core::ToJsValue;
// Static string, no allocation
let code: &'static str = js!{ 1 + 2 };
// Dynamic string due to interpolation
let x = 5;
let code: String = js!{ 1 + #{x} };§Compile-Time Error Detection
Invalid JavaScript produces clear compile-time errors:
// This will produce a compile-time error because the JavaScript is invalid
use viewpoint_js::js;
let code = js!{ function( };
// Error: unexpected end of input§Supported JavaScript Syntax
The macro supports a wide range of JavaScript syntax:
use viewpoint_js::js;
// Single-quoted strings
let code = js!{ document.querySelector('div') };
// Template literals
let code = js!{ `Hello ${name}` };
// Arrow functions
let code = js!{ (x) => x * 2 };
// Object literals
let code = js!{ { key: "value", nested: { x: 1 } } };
// Array literals
let code = js!{ [1, 2, 3].map(x => x * 2) };
// Regular expressions
let code = js!{ /pattern/gi };
// XPath expressions
let code = js!{ document.evaluate("//div", document) };
// Async/await
let code = js!{ async () => await fetch('/api') };
// Classes
let code = js!{ class Foo extends Bar { constructor() { super(); } } };§Integration with Viewpoint Core
The js! macro is designed for use with Viewpoint’s JavaScript evaluation:
ⓘ
use viewpoint_core::Page;
use viewpoint_js::js;
use viewpoint_js_core::ToJsValue;
// Evaluate simple expression
let width: i32 = page.evaluate(js!{ window.innerWidth }).await?;
// Evaluate with interpolation
let selector = "button.primary";
let result: serde_json::Value = page.evaluate(
&js!{ document.querySelector(#{selector})?.textContent }
).await?;
// Multi-line script
let items: Vec<String> = page.evaluate(js!{
Array.from(document.querySelectorAll("li"))
.map(el => el.textContent)
}).await?;Macros§
- js
- A macro that validates JavaScript syntax at compile time.