jsffi
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:
# 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 {.importcpp: "$$(#)".} # 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!") )
Imports
Types
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
JsAssoc[K; V] = ref object of JsRoot
- Statically typed wrapper around a JavaScript object. Source Edit
js = JsObject
- Source Edit
JsError {...}{.importc: "Error".} = object of JsRoot message*: cstring
- Source Edit
JsEvalError {...}{.importc: "EvalError".} = object of JsError
- 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: JsObject
- JavaScript's arguments pseudo-variable Source Edit
jsNull: JsObject
- JavaScript's null literal Source Edit
jsUndefined: JsObject
- JavaScript's undefined literal Source Edit
jsDirname: cstring
- JavaScript's __dirname pseudo-variable Source Edit
jsFilename: cstring
- JavaScript's __filename pseudo-variable Source Edit
Procs
proc toJsKey[T: SomeInteger](text: cstring; t: type T): T {...}{. importcpp: "parseInt(#)".}
- Source Edit
proc toJsKey[T: enum](text: cstring; t: type T): T
- Source Edit
proc toJsKey(text: cstring; t: type cstring): cstring
- Source Edit
proc toJsKey[T: SomeFloat](text: cstring; t: type T): T {...}{. importcpp: "parseFloat(#)".}
- Source Edit
proc isNull[T](x: T): bool {...}{.noSideEffect, importcpp: "(# === null)".}
- check if a value is exactly null Source Edit
proc isUndefined[T](x: T): bool {...}{.noSideEffect, importcpp: "(# === undefined)".}
- check if a value is exactly undefined Source Edit
proc newJsObject(): JsObject {...}{.importcpp: "{@}".}
- Creates a new empty JsObject Source Edit
proc newJsAssoc[K: JsKey; V](): JsAssoc[K, V] {...}{.importcpp: "{@}".}
- Creates a new empty JsAssoc with key type
K
and value typeV
. Source Edit proc hasOwnProperty(x: JsObject; prop: cstring): bool {...}{. importcpp: "#.hasOwnProperty(#)".}
- Checks, whether
x
has a property of nameprop
. Source Edit proc jsTypeOf(x: JsObject): cstring {...}{.importcpp: "typeof(#)".}
- Returns the name of the JsObject's JavaScript type as a cstring. Source Edit
proc jsNew(x: auto): JsObject {...}{.importcpp: "(new #)".}
- Turns a regular function call into an invocation of the JavaScript's
new
operator Source Edit proc jsDelete(x: auto): JsObject {...}{.importcpp: "(delete #)".}
- JavaScript's
delete
operator Source Edit proc require(module: cstring): JsObject {...}{.importc.}
- JavaScript's
require
function Source Edit proc to(x: JsObject; T: typedesc): T:type {...}{.importcpp: "(#)".}
- Converts a JsObject
x
to typeT
. Source Edit proc toJs[T](val: T): JsObject {...}{.importcpp: "(#)".}
- Converts a value of any type to type JsObject Source Edit
proc `&`(a, b: cstring): cstring {...}{.importcpp: "(# + #)".}
- Concatenation operator for JavaScript strings Source Edit
proc `+`(x, y: JsObject): JsObject {...}{.importcpp: "(# + #)".}
- Source Edit
proc `-`(x, y: JsObject): JsObject {...}{.importcpp: "(# - #)".}
- Source Edit
proc `*`(x, y: JsObject): JsObject {...}{.importcpp: "(# * #)".}
- Source Edit
proc `/`(x, y: JsObject): JsObject {...}{.importcpp: "(# / #)".}
- Source Edit
proc `%`(x, y: JsObject): JsObject {...}{.importcpp: "(# % #)".}
- Source Edit
proc `+=`(x, y: JsObject): JsObject {...}{.importcpp: "(# += #)", discardable.}
- Source Edit
proc `-=`(x, y: JsObject): JsObject {...}{.importcpp: "(# -= #)", discardable.}
- Source Edit
proc `*=`(x, y: JsObject): JsObject {...}{.importcpp: "(# *= #)", discardable.}
- Source Edit
proc `/=`(x, y: JsObject): JsObject {...}{.importcpp: "(# /= #)", discardable.}
- Source Edit
proc `%=`(x, y: JsObject): JsObject {...}{.importcpp: "(# %= #)", discardable.}
- Source Edit
proc `++`(x: JsObject): JsObject {...}{.importcpp: "(++#)".}
- Source Edit
proc `--`(x: JsObject): JsObject {...}{.importcpp: "(--#)".}
- Source Edit
proc `>`(x, y: JsObject): JsObject {...}{.importcpp: "(# > #)".}
- Source Edit
proc `<`(x, y: JsObject): JsObject {...}{.importcpp: "(# < #)".}
- Source Edit
proc `>=`(x, y: JsObject): JsObject {...}{.importcpp: "(# >= #)".}
- Source Edit
proc `<=`(x, y: JsObject): JsObject {...}{.importcpp: "(# <= #)".}
- Source Edit
proc `and`(x, y: JsObject): JsObject {...}{.importcpp: "(# && #)".}
- Source Edit
proc `or`(x, y: JsObject): JsObject {...}{.importcpp: "(# || #)".}
- Source Edit
proc `not`(x: JsObject): JsObject {...}{.importcpp: "(!#)".}
- Source Edit
proc `in`(x, y: JsObject): JsObject {...}{.importcpp: "(# in #)".}
- Source Edit
proc `[]`(obj: JsObject; field: cstring): JsObject {...}{.importcpp: "#[#]".}
- Return the value of a property of name
field
from a JsObjectobj
. Source Edit proc `[]`(obj: JsObject; field: int): JsObject {...}{.importcpp: "#[#]".}
- Return the value of a property of name
field
from a JsObjectobj
. Source Edit proc `[]=`[T](obj: JsObject; field: cstring; val: T) {...}{.importcpp: "#[#] = #".}
- Set the value of a property of name
field
in a JsObjectobj
tov
. Source Edit proc `[]=`[T](obj: JsObject; field: int; val: T) {...}{.importcpp: "#[#] = #".}
- Set the value of a property of name
field
in a JsObjectobj
tov
. Source Edit proc `[]`[K: JsKey; V](obj: JsAssoc[K, V]; field: K): V {...}{.importcpp: "#[#]".}
- Return the value of a property of name
field
from a JsAssocobj
. Source Edit proc `[]=`[K: JsKey; V](obj: JsAssoc[K, V]; field: K; val: V) {...}{. importcpp: "#[#] = #".}
- Set the value of a property of name
field
in a JsAssocobj
tov
. Source Edit proc `[]`[V](obj: JsAssoc[cstring, V]; field: string): V
- Source Edit
proc `[]=`[V](obj: JsAssoc[cstring, V]; field: string; val: V)
- Source Edit
proc `==`(x, y: JsRoot): bool {...}{.importcpp: "(# === #)".}
- Compare 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
Iterators
iterator pairs(obj: JsObject): (cstring, JsObject) {...}{.raises: [], tags: [].}
- Yields tuples of type
(cstring, JsObject)
, with the first entry being thename
of a fields in the JsObject and the second being its value wrapped into a JsObject. Source Edit iterator items(obj: JsObject): JsObject {...}{.raises: [], tags: [].}
- Yields the
values
of each field in a JsObject, wrapped into a JsObject. Source Edit iterator keys(obj: JsObject): cstring {...}{.raises: [], tags: [].}
- Yields the
names
of each field in 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 akey
in the JsAssoc and the second being its corresponding value. Source Edit iterator items[K, V](assoc: JsAssoc[K, V]): V
- Yields the
values
in a JsAssoc. Source Edit iterator keys[K: JsKey; V](assoc: JsAssoc[K, V]): K
- Yields the
keys
in a JsAssoc. Source Edit
Macros
macro jsFromAst(n: untyped): untyped
- Source Edit
macro `.`(obj: JsObject; field: untyped): JsObject
-
Experimental dot accessor (get) for type JsObject. Returns the value of a property of name
field
from a JsObjectx
.Example:
let obj = newJsObject() obj.a = 20 console.log(obj.a) # puts 20 onto the console.
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 JsObjectx
tovalue
. 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 withargs
as arguments, returning a JsObject (which may be discarded, and may beundefined
, 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](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 JsObjectx
. 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 JsObjectx
tovalue
. 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 withargs
as arguments. Here, everything is typechecked, so you do not have to worry aboutundefined
return values. Source Edit macro `{}`(typ: typedesc; xs: varargs[untyped]): auto
-
Takes a
typedesc
as its first argument, and a series of expressions of typekey: value
, and returns a value of the specified type with each fieldkey
set tovalue
, 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 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
Templates
© 2006–2021 Andreas Rumpf
Licensed under the MIT License.
https://nim-lang.org/docs/jsffi.html