9 JavaScript Transformation
| (require racket-webview/private/js-transform) | |
| package: racket-webview | |
This module provides a small compile-time DSL for generating JavaScript source text from Racket-like forms. It is used internally by the webview implementation to construct JavaScript snippets in a structured way, without having to write the complete snippet as one raw string.
The module exports only js. The other forms described in this section are DSL forms recognized by js; they are not exported as separate bindings.
syntax
(js js-statement ...)
Each top-level statement is emitted with a trailing semicolon and newline. The resulting string can be passed to the webview layer for injection or execution in the browser context.
For example:
(define source (js (set! window.myfunc (λ (x) (let* ((el (send document getElementById 'hi)) (y (* x x))) (send el setAttribute "x" (+ y ""))) (send console log "did set attribute x on element hi")))))
This generates JavaScript source that assigns a function to window.myfunc. The function looks up a DOM element, calculates a value, sets an attribute, and writes a message to the JavaScript console.
9.1 Primitive values
Numbers are emitted as JavaScript numeric literals. Strings are emitted as double-quoted JavaScript strings, with embedded double quotes escaped. Identifiers are emitted as JavaScript names.
For example:
(js (send console log "hello") (send console log 42))
Symbols can be quoted to produce JavaScript string values:
(js (send console log 'hello))
9.2 Function calls and method calls
A form that is not recognized as a special DSL form is treated as a JavaScript function call:
(js (alert "Hello from Racket"))
This generates a JavaScript call to alert.
Method calls can be written with send:
(js (send document getElementById 'hi))
The send form has the shape:
(send object method arg ...)
and generates a JavaScript method call of the form:
object.method(arg, ...)
For example, (send document getElementById 'hi) generates a call shaped like:
document.getElementById("hi")
Using send keeps the method-call structure explicit in the DSL. It is usually clearer than writing dotted JavaScript names directly as function names.
9.3 Operators
The DSL supports a small set of JavaScript infix operators.
(+ a b ...), (- a b ...), (* a b ...) and (/ a b ...) generate arithmetic infix expressions.
(and a b ...) and (or a b ...) generate JavaScript && and || expressions.
(> a b), (< a b), (>= a b), (<= a b), (== a b), (=== a b) and (!= a b) generate JavaScript comparison expressions.
For example:
(js (define (inside-range x) (if (and (> x 10) (< x 15)) (return x) (return (* x x)))))
9.4 Definitions and assignments
syntax
(define (name arg ...) body ...)
(js (define (square x) (return (* x x))))
This produces a JavaScript function named square.
syntax
(set! name expr)
(js (set! window.answer 42))
This produces an assignment to window.answer.
9.5 Functions
syntax
(lambda (arg ...) body ...)
syntax
(λ (arg ...) body ...)
For example:
(js (set! window.square (λ (x) (return (* x x)))))
A function body may contain more than one DSL statement:
(js (set! window.f (λ (x) (send console log x) (return (* x x)))))
9.6 Control flow and statement blocks
syntax
(if condition then-expr else-expr)
(js (define (f x) (if (> x 0) (return x) (return (- 0 x)))))
syntax
(begin body ...)
(js (if (> x 10) (begin (send console log x) (return x)) (return 0)))
(js (define (id x) (return x)))
9.7 Sequential bindings
syntax
(let* ((id expr) ...) body ...)
(js (let* ((x 10) (y (* x x))) (send console log y) (return y)))
The plain let form is intentionally not supported in a JavaScript context. Use let* instead, so that the generated JavaScript bindings remain explicitly sequential.
9.8 Lists
syntax
(list expr ...)
(js (send console log (list 1 2 3)))
syntax
(cons expr list-expr)
For example:
(js (send console log (cons x (cons y (list z)))))
This generates JavaScript shaped like:
console.log([ x].concat([ y].concat([ z])));
9.9 Embedding Racket values
syntax
(eval value)
(define names '(alice bob charlie)) (js (send console log (eval names)))
The value is converted to JavaScript source text before the generated JavaScript is returned.
9.10 Example
The following example combines function definition, method calls, list construction, sequential bindings and a lambda expression passed to a JavaScript method:
(define source (js (define (f x y z) (send console log (cons x (cons y (list z)))) (let* ((l (cons x (cons y (list z))))) (return (send l map (λ (a) (return (+ a 10)))))))))
This generates a JavaScript function that logs an array, constructs the same array in a local binding, maps over it, and returns the mapped result.
9.11 Limitations
This transformer is intentionally small. It is not a complete JavaScript parser, not a JavaScript evaluator and not a general Racket-to-JavaScript compiler. Unrecognized forms are generally treated as JavaScript function calls. This keeps the implementation compact, but it also means that some mistakes may produce JavaScript source that only fails when the browser runs the generated code.
The generated source is intended for small snippets used by the webview layer, not for translating arbitrary Racket programs to JavaScript.