Translates Elixir expressions to JavaScript.
This module handles the core transpilation of Elixir AST to JavaScript code, enabling optimistic client-side evaluation of reactive expressions.
Supported Constructs
Literals
- Strings, numbers, booleans, nil, atoms
- Lists and maps
Operators
- Comparison:
==,!=,>,<,>=,<= - Logical:
&&,||,and,or,not,! - Arithmetic:
+,-,*,/ - String:
<>(concatenation) - List:
++(concatenation),in(membership) - Pipe:
|>
Control Flow
if cond, do: x, else: y→ ternary operator
State References
@variable→state.variable@nested.field→state.nested.field
String Functions
String.length/1,String.trim/1String.to_integer/1,String.to_float/1String.contains?/2,String.starts_with?/2,String.ends_with?/2String.replace/3,String.slice/3,String.match?/2
Enum Functions
Enum.member?/2,Enum.count/1,Enum.join/1,2Enum.map/2,Enum.filter/2,Enum.reject/2
Map Functions
Map.get/2,3
Utility Functions
Usage
iex> Lavash.Transpiler.to_js("@count + 1")
"(state.count + 1)"
iex> Lavash.Transpiler.to_js("if @active, do: \"on\", else: \"off\"")
"(state.active ? \"on\" : \"off\")"Validation
Use validate/1 to check if an expression can be fully transpiled:
iex> Lavash.Transpiler.validate("@count + 1")
:ok
iex> Lavash.Transpiler.validate("Ash.read!(Product)")
{:error, "Ash.read!"}
Summary
Functions
Translates an Elixir AST to JavaScript.
Translates an Elixir expression string to JavaScript.
Checks if an AST can be transpiled to JavaScript.
Transpiles a run function body to JavaScript statements.
Validates that an Elixir expression can be transpiled to JavaScript.
Validates an AST for transpilability.
Functions
Translates an Elixir AST to JavaScript.
This is the lower-level function that works directly with AST.
Use to_js/1 for string input.
Translates an Elixir expression string to JavaScript.
Returns the JavaScript code as a string. Untranspilable expressions
are converted to undefined with a comment indicating the issue.
Examples
iex> Lavash.Transpiler.to_js("@count")
"state.count"
iex> Lavash.Transpiler.to_js("length(@items)")
"(state.items.length)"
iex> Lavash.Transpiler.to_js("if @a, do: 1, else: 2")
"(state.a ? 1 : 2)"
Checks if an AST can be transpiled to JavaScript.
Returns true if transpilable, false otherwise.
Use validate/1 or validate_ast/1 to get the specific error.
Transpiles a run function body to JavaScript statements.
This handles the run [:reads], fn assigns -> ... end pattern, where:
assigns.fieldaccesses →state.fieldx = exprbindings →const x = expr;assign(assigns, :field, value)→ adds to return object- Piped assigns with multiple
assign/3calls → combined return object
Returns a tuple of {statements, return_expr} where statements is a list
of JS statements (const declarations) and return_expr is the return object.
Examples
# Simple case
iex> body = quote do
...> discount = assigns.subtotal * assigns.discount_rate
...> assigns |> assign(:discount_amount, discount) |> assign(:total, assigns.subtotal - discount)
...> end
iex> Lavash.Rx.Transpiler.transpile_run_body(body)
{["const discount = (state.subtotal * state.discount_rate);"],
"{discount_amount: discount, total: (state.subtotal - discount)}"}
Validates that an Elixir expression can be transpiled to JavaScript.
Returns :ok if the expression is fully transpilable, or
{:error, description} if it contains unsupported constructs.
Examples
iex> Lavash.Rx.Transpiler.validate("length(@tags)")
:ok
iex> Lavash.Rx.Transpiler.validate("Ash.read!(Product)")
{:error, "Ash.read!"}
Validates an AST for transpilability.
Returns :ok if transpilable, {:error, reason} otherwise.