std/jsffi

Search:
Source   Edit  

This Module implements types and macros to facilitate the wrapping of, and interaction with JavaScript libraries. Using the provided types JsObject and JsAssoc together with the provided macros allows for smoother interfacing with JavaScript, allowing for example quick and easy imports of JavaScript variables:

Example:

import std/jsffi
# Here, we are using jQuery for just a few calls and do not want to wrap the
# whole library:

# import the document object and the console
var document {.importc, nodecl.}: JsObject
var console {.importc, nodecl.}: JsObject
# import the "$" function
proc jq(selector: JsObject): JsObject {.importjs: "$$(#)".}

# Use jQuery to make the following code run, after the document is ready.
# This uses an experimental `.()` operator for `JsObject`, to emit
# JavaScript calls, when no corresponding proc exists for `JsObject`.
proc main =
  jq(document).ready(proc() =
    console.log("Hello JavaScript!")
  )

Types

js = JsObject
Source   Edit  
JsAssoc[K; V] = ref object of JsRoot
  
Statically typed wrapper around a JavaScript object. Source   Edit  
JsError {.importc: "Error".} = object of JsRoot
  message*: cstring
Source   Edit  
JsEvalError {.importc: "EvalError".} = object of JsError
Source   Edit  
JsKey = concept atypeof(T)
    cstring.toJsKey(T) is T
Source   Edit  
JsObject = ref object of JsRoot
  
Dynamically typed wrapper around a JavaScript object. Source   Edit  
JsRangeError {.importc: "RangeError".} = object of JsError
Source   Edit  
JsReferenceError {.importc: "ReferenceError".} = object of JsError
Source   Edit  
JsSyntaxError {.importc: "SyntaxError".} = object of JsError
Source   Edit  
JsTypeError {.importc: "TypeError".} = object of JsError
Source   Edit  
JsURIError {.importc: "URIError".} = object of JsError
Source   Edit  

Vars

jsArguments {.importc: "arguments", nodecl.}: JsObject
JavaScript's arguments pseudo-variable. Source   Edit  
jsDirname {.importc: "__dirname", nodecl.}: cstring
JavaScript's __dirname pseudo-variable. Source   Edit  
jsFilename {.importc: "__filename", nodecl.}: cstring
JavaScript's __filename pseudo-variable. Source   Edit  
jsNull {.importc: "null", nodecl.}: JsObject
JavaScript's null literal. Source   Edit  
jsUndefined {.importc: "undefined", nodecl.}: JsObject
JavaScript's undefined literal. Source   Edit  

Procs

proc `%`(x, y: JsObject): JsObject {.importjs: "(# % #)", ...raises: [], tags: [],
                                     forbids: [].}
Source   Edit  
proc `%=`(x, y: JsObject): JsObject {.importjs: "(# %= #)", discardable,
                                      ...raises: [], tags: [], forbids: [].}
Source   Edit  
proc `&`(a, b: cstring): cstring {.importjs: "(# + #)", ...raises: [], tags: [],
                                   forbids: [].}
Concatenation operator for JavaScript strings. Source   Edit  
proc `*`(x, y: JsObject): JsObject {.importjs: "(# * #)", ...raises: [], tags: [],
                                     forbids: [].}
Source   Edit  
proc `**`(x, y: JsObject): JsObject {.importjs: "((#) ** #)", ...raises: [],
                                      tags: [], forbids: [].}
Source   Edit  
proc `*=`(x, y: JsObject): JsObject {.importjs: "(# *= #)", discardable,
                                      ...raises: [], tags: [], forbids: [].}
Source   Edit  
proc `+`(x, y: JsObject): JsObject {.importjs: "(# + #)", ...raises: [], tags: [],
                                     forbids: [].}
Source   Edit  
proc `++`(x: JsObject): JsObject {.importjs: "(++#)", ...raises: [], tags: [],
                                   forbids: [].}
Source   Edit  
proc `+=`(x, y: JsObject): JsObject {.importjs: "(# += #)", discardable,
                                      ...raises: [], tags: [], forbids: [].}
Source   Edit  
proc `-`(x, y: JsObject): JsObject {.importjs: "(# - #)", ...raises: [], tags: [],
                                     forbids: [].}
Source   Edit  
proc `--`(x: JsObject): JsObject {.importjs: "(--#)", ...raises: [], tags: [],
                                   forbids: [].}
Source   Edit  
proc `-=`(x, y: JsObject): JsObject {.importjs: "(# -= #)", discardable,
                                      ...raises: [], tags: [], forbids: [].}
Source   Edit  
proc `/`(x, y: JsObject): JsObject {.importjs: "(# / #)", ...raises: [], tags: [],
                                     forbids: [].}
Source   Edit  
proc `/=`(x, y: JsObject): JsObject {.importjs: "(# /= #)", discardable,
                                      ...raises: [], tags: [], forbids: [].}
Source   Edit  
proc `<`(x, y: JsObject): JsObject {.importjs: "(# < #)", ...raises: [], tags: [],
                                     forbids: [].}
Source   Edit  
proc `<=`(x, y: JsObject): JsObject {.importjs: "(# <= #)", ...raises: [],
                                      tags: [], forbids: [].}
Source   Edit  
proc `==`(x, y: JsRoot): bool {.importjs: "(# === #)", ...raises: [], tags: [],
                                forbids: [].}
Compares two JsObjects or JsAssocs. Be careful though, as this is comparison like in JavaScript, so if your JsObjects are in fact JavaScript Objects, and not strings or numbers, this is a comparison of references. Source   Edit  
proc `>`(x, y: JsObject): JsObject {.importjs: "(# > #)", ...raises: [], tags: [],
                                     forbids: [].}
Source   Edit  
proc `>=`(x, y: JsObject): JsObject {.importjs: "(# >= #)", ...raises: [],
                                      tags: [], forbids: [].}
Source   Edit  
proc `[]`(obj: JsObject; field: cstring): JsObject {.importjs: "#[#]",
    ...raises: [], tags: [], forbids: [].}
Returns the value of a property of name field from a JsObject obj. Source   Edit  
proc `[]`(obj: JsObject; field: int): JsObject {.importjs: "#[#]", ...raises: [],
    tags: [], forbids: [].}
Returns the value of a property of name field from a JsObject obj. Source   Edit  
proc `[]`[K: JsKey; V](obj: JsAssoc[K, V]; field: K): V {.importjs: "#[#]",
    ...raises: [], tags: [], forbids: [].}
Returns the value of a property of name field from a JsAssoc obj. Source   Edit  
proc `[]`[V](obj: JsAssoc[cstring, V]; field: string): V
Source   Edit  
proc `[]=`[K: JsKey; V](obj: JsAssoc[K, V]; field: K; val: V) {.
    importjs: "#[#] = #", ...raises: [], tags: [], forbids: [].}
Sets the value of a property of name field in a JsAssoc obj to v. Source   Edit  
proc `[]=`[T](obj: JsObject; field: cstring; val: T) {.importjs: "#[#] = #",
    ...raises: [], tags: [], forbids: [].}
Sets the value of a property of name field in a JsObject obj to v. Source   Edit  
proc `[]=`[T](obj: JsObject; field: int; val: T) {.importjs: "#[#] = #",
    ...raises: [], tags: [], forbids: [].}
Sets the value of a property of name field in a JsObject obj to v. Source   Edit  
proc `[]=`[V](obj: JsAssoc[cstring, V]; field: string; val: V)
Source   Edit  
proc `and`(x, y: JsObject): JsObject {.importjs: "(# && #)", ...raises: [],
                                       tags: [], forbids: [].}
Source   Edit  
proc hasOwnProperty(x: JsObject; prop: cstring): bool {.
    importjs: "#.hasOwnProperty(#)", ...raises: [], tags: [], forbids: [].}
Checks, whether x has a property of name prop. Source   Edit  
proc `in`(x, y: JsObject): JsObject {.importjs: "(# in #)", ...raises: [],
                                      tags: [], forbids: [].}
Source   Edit  
proc isNull[T](x: T): bool {.noSideEffect, importjs: "(# === null)", ...raises: [],
                             tags: [], forbids: [].}
Checks if a value is exactly null. Source   Edit  
proc isUndefined[T](x: T): bool {.noSideEffect, importjs: "(# === undefined)",
                                  ...raises: [], tags: [], forbids: [].}
Checks if a value is exactly undefined. Source   Edit  
proc jsDelete(x: auto): JsObject {.importjs: "(delete #)", ...raises: [], tags: [],
                                   forbids: [].}
JavaScript's delete operator. Source   Edit  
proc jsNew(x: auto): JsObject {.importjs: "(new #)", ...raises: [], tags: [],
                                forbids: [].}
Turns a regular function call into an invocation of the JavaScript's new operator. Source   Edit  
proc jsTypeOf(x: JsObject): cstring {.importjs: "typeof(#)", ...raises: [],
                                      tags: [], forbids: [].}
Returns the name of the JsObject's JavaScript type as a cstring. Source   Edit  
proc newJsAssoc[K: JsKey; V](): JsAssoc[K, V] {.importjs: "{@}", ...raises: [],
    tags: [], forbids: [].}
Creates a new empty JsAssoc with key type K and value type V. Source   Edit  
proc newJsObject(): JsObject {.importjs: "{@}", ...raises: [], tags: [],
                               forbids: [].}
Creates a new empty JsObject. Source   Edit  
proc `not`(x: JsObject): JsObject {.importjs: "(!#)", ...raises: [], tags: [],
                                    forbids: [].}
Source   Edit  
proc `or`(x, y: JsObject): JsObject {.importjs: "(# || #)", ...raises: [],
                                      tags: [], forbids: [].}
Source   Edit  
proc require(module: cstring): JsObject {.importc, ...raises: [], tags: [],
    forbids: [].}
JavaScript's require function. Source   Edit  
proc to(x: JsObject; T: typedesc): T:type {.importjs: "(#)", ...raises: [],
    tags: [], forbids: [].}
Converts a JsObject x to type T. Source   Edit  
proc toJs[T](val: T): JsObject {.importjs: "(#)", ...raises: [], tags: [],
                                 forbids: [].}
Converts a value of any type to type JsObject. Source   Edit  
proc toJsKey(text: cstring; t: type cstring): cstring
Source   Edit  
proc toJsKey[T: enum](text: cstring; t: type T): T
Source   Edit  
proc toJsKey[T: SomeFloat](text: cstring; t: type T): T {.
    importjs: "parseFloat(#)", ...raises: [], tags: [], forbids: [].}
Source   Edit  
proc toJsKey[T: SomeInteger](text: cstring; t: type T): T {.
    importjs: "parseInt(#)", ...raises: [], tags: [], forbids: [].}
Source   Edit  

Iterators

iterator items(obj: JsObject): JsObject {....raises: [], tags: [], forbids: [].}
Yields the values of each field in a JsObject, wrapped into a JsObject. Source   Edit  
iterator items[K, V](assoc: JsAssoc[K, V]): V
Yields the values in a JsAssoc. Source   Edit  
iterator keys(obj: JsObject): cstring {....raises: [], tags: [], forbids: [].}
Yields the names of each field in a JsObject. Source   Edit  
iterator keys[K: JsKey; V](assoc: JsAssoc[K, V]): K
Yields the keys in a JsAssoc. Source   Edit  
iterator pairs(obj: JsObject): (cstring, JsObject) {....raises: [], tags: [],
    forbids: [].}
Yields tuples of type (cstring, JsObject), with the first entry being the name of a fields in the JsObject and the second being its value wrapped into a JsObject. Source   Edit  
iterator pairs[K: JsKey; V](assoc: JsAssoc[K, V]): (K, V)
Yields tuples of type (K, V), with the first entry being a key in the JsAssoc and the second being its corresponding value. Source   Edit  

Macros

macro `.`(obj: JsObject; field: untyped): JsObject
Experimental dot accessor (get) for type JsObject. Returns the value of a property of name field from a JsObject x.

Example:

let obj = newJsObject()
obj.a = 20
assert obj.a.to(int) == 20
Source   Edit  
macro `.`[K: cstring; V](obj: JsAssoc[K, V]; field: untyped): V
Experimental dot accessor (get) for type JsAssoc. Returns the value of a property of name field from a JsObject x. Source   Edit  
macro `.()`(obj: JsObject; field: untyped; args: varargs[JsObject, jsFromAst]): JsObject

Experimental "method call" operator for type JsObject. Takes the name of a method of the JavaScript object (field) and calls it with args as arguments, returning a JsObject (which may be discarded, and may be undefined, if the method does not return anything, so be careful when using this.)

Example:

# Let's get back to the console example:
var console {.importc, nodecl.}: JsObject
let res = console.log("I return undefined!")
console.log(res) # This prints undefined, as console.log always returns
                 # undefined. Thus one has to be careful, when using
                 # JsObject calls.

Source   Edit  
macro `.()`[K: cstring; V: proc](obj: JsAssoc[K, V]; field: untyped;
                                 args: varargs[untyped]): auto
Experimental "method call" operator for type JsAssoc. Takes the name of a method of the JavaScript object (field) and calls it with args as arguments. Here, everything is typechecked, so you do not have to worry about undefined return values. Source   Edit  
macro `.=`(obj: JsObject; field, value: untyped): untyped
Experimental dot accessor (set) for type JsObject. Sets the value of a property of name field in a JsObject x to value. Source   Edit  
macro `.=`[K: cstring; V](obj: JsAssoc[K, V]; field: untyped; value: V): untyped
Experimental dot accessor (set) for type JsAssoc. Sets the value of a property of name field in a JsObject x to value. Source   Edit  
macro bindMethod(procedure: typed): auto

Takes the name of a procedure and wraps it into a lambda missing the first argument, which passes the JavaScript builtin this as the first argument to the procedure. Returns the resulting lambda.

Example:

We want to generate roughly this JavaScript:

var obj = {a: 10};
obj.someMethod = function() {
  return this.a + 42;
};

We can achieve this using the bindMethod macro:

let obj = JsObject{ a: 10 }
proc someMethodImpl(that: JsObject): int =
  that.a.to(int) + 42
obj.someMethod = bindMethod someMethodImpl

# Alternatively:
obj.someMethod = bindMethod
  proc(that: JsObject): int = that.a.to(int) + 42

Source   Edit  
macro jsFromAst(n: untyped): untyped
Source   Edit  
macro `{}`(typ: typedesc; xs: varargs[untyped]): auto

Takes a typedesc as its first argument, and a series of expressions of type key: value, and returns a value of the specified type with each field key set to value, as specified in the arguments of {}.

Example:

# Let's say we have a type with a ton of fields, where some fields do not
# need to be set, and we do not want those fields to be set to `nil`:
type
  ExtremelyHugeType = ref object
    a, b, c, d, e, f, g: int
    h, i, j, k, l: cstring
    # And even more fields ...

let obj = ExtremelyHugeType{ a: 1, k: "foo".cstring, d: 42 }

# This generates roughly the same JavaScript as:
{.emit: "var obj = {a: 1, k: "foo", d: 42};".}

Source   Edit  

Templates

template toJs(s: string): JsObject
Source   Edit