lexifi / gen_js_api Goto Github PK
View Code? Open in Web Editor NEWEasy OCaml bindings for Javascript libraries
License: MIT License
Easy OCaml bindings for Javascript libraries
License: MIT License
I have something like this…
module rec Contact : sig
type t
val get_contact_groups: t -> ContactGroup.t list [@@js.call]
end
and ContactGroup : sig
type t
val get_contacts: t -> Contact.t list [@@js.call]
end
I solved the problem by getting type Contact.t out of Contact module.
But I do not know if this solution will always be enough?
Best regards
Hi,
I'm currently writing a binding using gen_js_api and it seems that this code doesn't compile :
type t = [`foo | `bar [@js 42] | `Baz] [@@js.enum]
(* Throws this exception : Sum types without js.* attribute not supported in this context. *)
(* While this compile *)
val foo : t -> ([ `a | `b] [@js.enum]) -> ...
After looking at gen_js_api.ml
, the exception is raised by get_variant_kind
because attrs
doesn't contain the attribute "js.enum"
attached to t
. In fact the attribute js.enum
is added to global_attrs
in gen_funs
and get_variant_kind
doesn't look inside.
The workaround I use for now is to match against the return value of parse_typ
to see if it is a Variant
, then in this case I manually add the attribute to it, but it's a bit of a hack.
| Some ty, Ptype_abstract ->
let ty = match parse_typ ~global_attrs ty with
| Variant { location; global_attrs; attributes; constrs } ->
Variant { location; global_attrs; attributes = p.ptype_attributes @ attributes; constrs }
| ty -> ty
in
lazy (js2ml_fun ty), lazy (ml2js_fun ty)
I hope I clearly described this issue,
Thank you!
When mapping a JS array to OCaml array with gen_js_api, a new object is instantiated:
let n = int_of_js (get objs "length") in
Array.init (n - start) (fun i -> f (array_get objs (start + i)))
A new object is also created when mapping OCaml array to JS:
let array_make n = new_obj (get global "Array") [|int_to_js n|]
let array_to_js f arr =
let n = Array.length arr in
let a = array_make n in
for i = 0 to n - 1 do
array_set a i (f arr.(i))
done;
a
I want to map a JS object (which has fields of type 'array') to OCaml:
module Dataset : sig
(* JS object *)
type t
val data : t -> float array
val set_data : t -> float array -> unit
(* Some other bindings here *)
val make : ?data:float array ->
(* Some other bindings here *)
unit ->
t [@@js.builder]
Javascript object t
is used by an external JS library to render data on a chart. When the data
array is mutated, the chart is re-rendered.
The problem here is that when I do such a mapping, a new data
array is created, and all changes are made with a new object, not the original one. Original array stay untouched. Mutating this new array causes no re-render of a chart because it is not a part of t
object. To force re-render, I should not only mutate the new array, but also set this new array to t
object via set data
function. This seems like a great overhead.
What is the best way to handle this problem?
Is there any way to make "direct" mapping between JS and OCaml arrays without new allocations?
Hi
I have one package containing a library of a small set of jquery bindings and a test directory. I have a second package containing a library in which there is a dependency on the first library.
The test in the first package can quite happily call Jquery.selector string
in the library. If I make the same call from the second library in the second package then a Ojs_exn.Error(_)
is thrown.
Both packages compile without error using a dune environment and gen_js_api 1.0.5. I can call non-binding
functions in the first library from the second library without error - so the first library is visible to the second library.
There is also another binding in the second library. The test directory in the second package can also call the bindings in the second library without error, but, again, none of the bindings in the first library.
I've spent just over a day on this now and I've run out of ideas on how to fix this - can anyone think of any possible suggestions?
Thanks
Nick
If there's no suffix to the function name (so it's simply called new_
) and there's no explicit name given to [@@js.new]
, the class name seems to be infered as ""
.
For instace this:
val new_ : ArrayBuffer.t -> t [@@js.new]
Generates:
function new$0(x9)
{var _c0_=caml_call1(ArrayBuffer[2],x9);return new (Ojs[16].)(_c0_)}
Which produces an invalid syntax error when run with node
. It would be nice if the compilation failed with an error instead.
Hello,
I meet a problem when generating an .ml file with my .mli
When implementing a function containing ppx syntax token with [@@js.custom]
, I get an error compiling the .ml file. Here an example.
In my mli file, I have:
[@@js.custom
let foo str =
(Js.Unsafe.global##.myfield := str)
]
The generated ml file interprets the ##.
as an infix operator, so it writes in the ml file :
(##. Js.Unsafe.global myfield) := str
Then, I get a syntax error when compiling.
Is there a way to use other packages ppx syntax within the mli file?
Currently is not allowed to use the "open" statement in a signature parsed by gen_js_api.
I do not think there is a strong reason not to allow it, but I may be wrong.
Note that, currently, there's a work around:
[@@@js.stop]
open M
[@@@js.start]
[@@@js.implem open M]
Just a quick question - I'm trying to do bindings for Promise and I'm having trouble in setting up parametrisation - starting out with the notes in TYPES.md:
type 'a t
val t_to_js: ('a -> Ojs.t) -> 'a t -> Ojs.t
val t_of_js: (Ojs.t -> 'a) -> Ojs.t -> 'a t
I then compile with:
ocamlfind gen_js_api/gen_js_api js_promise.mli
and get:
File "js_promise.mli", line 3, characters 23-25:
Error: Cannot parse type
It seems that it doesn't like the 'a
. Is there a work-around for this?
Hello,
I get an error The external function
caml_ojs_new_arr' is not available` with this .mli:
class _lat_lng : Ojs.t ->
object
inherit Ojs.obj
end
class lat_lng: lat:float -> lng:float -> ?no_wrap:bool -> unit -> _lat_lng
% ocamlfind gen_js_api/gen_js_api googleMaps.mli
% sh build_examples.sh
+ ocamlfind ocamlc -c -safe-string -w A -w -E -package js_of_ocaml.ppx
-package js_of_ocaml -package gen_js_api -package lwt.ppx -I examples
-o examples/main.cmo examples/main.ml
File "examples/main.ml", line 6, characters 6-13:
Warning 26: unused variable element.
+ ocamlfind ocamlc -linkpkg -package js_of_ocaml.ppx -package js_of_ocaml
-package gen_js_api -package lwt.ppx googleMaps.cmo examples/main.cmo
-o examples/main.byte
File "_none_", line 1:
Error: Error while linking googleMaps.cmo:
The external function `caml_ojs_new_arr' is not available
Command exited with code 2.
If i replace the last line of the .mli file with this one, it's ok:
class lat_lng: float -> float -> bool option -> _lat_lng
% ocamlfind gen_js_api/gen_js_api googleMaps.mli
% sh build_examples.sh
+ ocamlfind ocamlc -c -safe-string -w A -w -E -package js_of_ocaml.ppx
-package js_of_ocaml -package gen_js_api -package lwt.ppx -I examples
-o examples/main.cmo examples/main.ml
File "examples/main.ml", line 6, characters 6-13:
Warning 26: unused variable element.
Finished, 7 targets (0 cached) in 00:00:00.
There are some missing primitives
Dummy implementations (raising 'Failure' exception) will be used if they are not
available at runtime. You can prevent the generation of dummy implementations
with the commandline option '--disable genprim'
Missing primitives:
caml_hexstring_of_float
Currently the documentation says
It is possible to specify constructors with one argument of
type either int or string, used to represent "all other cases" of JS values.
I'd like to be able to say
type status =
type t =
| String of string [@js.default]
| Number of float [@js.default]
[@@js.enum]
Currently:
% make
make -C src all
make[1]: Entering directory '/tmp/gen_js_api.1.0.5/src'
ocamlc -w +A-4-41-45 -warn-error +8 -c ojs.mli ojs.ml
ocamlc -w +A-4-41-45 -warn-error +8 -c ojs_exn.mli ojs_exn.ml
ocamlc -w +A-4-41-45 -warn-error +8 -a -o gen_js_api.cma ojs.cmo ojs_exn.cmo
ocamlc -w +A-4-41-45 -warn-error +8 -I +compiler-libs -o gen_js_api ocamlcommon.cma gen_js_api.mli gen_js_api.ml
File "gen_js_api.ml", line 156, characters 76-79:
156 | | Error (loc, err) -> Some (Location.error_of_printer loc print_error err)
^^^
Error: The function applied to this argument has type
?loc:Location.t -> ?sub:Location.msg list -> Location.report
This argument cannot be applied without label
In practice, it means that caml_js_get
, caml_js_set
, caml_js_delete
will behave differently if called with non ascii strings.
Here are possible moves
Ojs.get
, Ojs.set
, Ojs.delete
with a wrapperlet get o s = get o (string_to_js s)
caml_js_get_string_key
, caml_js_set_string_key
, caml_js_delete_string_key
)Could you publish the update of gen_js_api in the opam repository https://github.com/ocaml/opam-repository? In this way, we don't need anymore to pin the repository to resolve the issue #58. A Cordova binding needs the current version.
For vscode-ocaml-platform, we used [@@js.*] attributes on val declarations in implementation files that are described in PPX.md.
After trying to update to 1.0.7, the val declarations stopped compiling. The example in PPX.md has the following error:
1 | val alert_bool : bool -> unit [@@js.global "alert"]
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Error: Value declarations are only allowed in signatures
In 1.0.6, this compiled without errors. As a workaround, each block of val declarations can be wrapped in include [%js: ... ]
, but that makes the code much messier when mixed with normal OCaml functions.
The following input:
include [%js:
module T: sig
val log: 'a -> unit [@@js.global "jsLog"]
end [@js.scope "jsT"]
]
errors out with Spurious js.* attribute
.
I would expect it to behave the same as:
include [%js:
module T: sig
val log: 'a -> unit [@@js.global] (* Note the absence of payload here *)
end [@js.scope "jsT"]
]
And generate the following code (for the original input):
module T = struct
let (log : 'a -> unit) =
fun x1 -> ignore (Ojs.call (Ojs.get Ojs.global "jsT") "jsLog" [|Obj.magic x1|])
end
See the discussion of #144 .
This project looks really great. I'm curious, why does it only support 4.03
and above?
Considering a javascript library containing a module A with only one static
function called foo taking a string and returning an int. This library
has to be required before usage, but I did not found anything about require in
the documentation.
The first implementation I made was:
module A : sig
type t
val require_a : unit -> t
[@@js.custom let require_a () = Helpers.require "A" ]
val foo : t -> string -> int
end
To call the function foo in ocaml code, I have to write:
let module_a = A.require_a () in
let i = A.foo module_a "bar" in
...
But I'd like to get rid of the require line. This require would be done lazily into the
module, allowing the user to write directly let i = A.foo "bar"
to call the
function.
My first thought was to do something like:
module A : sig
[@@@js.implem
let mod_a = ref None
let get_a () =
match !mod_a with
| Some m -> m
| None ->
let m = Helpers.require "A" in
mode_a := Some m;
m
]
val foo : string -> int
[@@js.custom
let foo str =
let m = get_a () in
let r = Ojs.call m "foo" [|(Ojs.string_to_js str)|] in
Ojs.int_of_js r ]
end
But it becomes very annoying to write this when the javascript library has many
methods. What would be the best way to do this kind of things ? Does it match
the philosophy of javascript bindings ?
Having seen Seb's recent JS's Date
bindings, they look great.
However something I feel is missing is a way of generating links to documentation so that ocamldoc
'd mli
s have direct links to the documentation of the bound function (see e.g. the documentation of functions in tgls, automatically generated, or tsdl, manually done with a lot of query-replace-regexp
). These are small touches but they have a huge productivity impact when you are working with the binding.
Most of the time it is sufficient to be able to define an URI pattern where a variable is substituted by the bound method name (and the ability to override in case of exceptions). Now one of the problem is that you don't generate the mli
s. If your eventual goal is to generate mli
's from other descriptions then this could actually be done at that time, otherwise you might consider generating mli
's aswell from the annotated mli
s and provide support for automatically generating doc strings.
The other problem is to find a good and reliable source of documentation for the browser APIs, directly linking on the standards may be a little bit dry. I tend to use mozilla's developer documentation which has links to the standards and information about browser support. See e.g. the Date
documentation.
I find myself doing this quite often in mli
files that are processed with gen_js_api:
val use_state: (unit -> 'state) -> ('state * ('state -> unit))
[@@js.custom
val use_state_internal : (unit -> Ojs.t) -> (Ojs.t * (Ojs.t -> unit)) [@@js.global "react.useState"]
let use_state = Obj.magic use_state_internal
]
Binding to functions with type variables is very common, and regular external
statements support it.
The manual process shown above is verbose and error prone, and I was wondering if it'd be possible for gen_js_api to interpret 'a
as the black box that it is, that doesn't require any conversions (or rather because it can be any type, the only function applicable is identity), and basically automate the "magic" that is needed for those kind of signatures to exist.
I am trying to use mli
files with gen_js_api to write a binding to a JS function that has type
val foo: 'a array -> unit
With raw externals, one can use parametrized types as input, but with gen_js_api there's the error Error: Cannot parse type
.
I tried to use Ojs
as well, but because in this case there's no "creation function", I don't think it's possible, unless one writes converters for every potential type that will fill 'a
, which is not convenient.
Is there a way to achieve this using an interface file?
Related to #77.
Hello,
Do you plan to add a .d.ts -> .mli converter ? (does it make sense ?)
This could be very useful to use declarations from https://github.com/borisyankov/DefinitelyTyped instead of writing all .mli file by hand.
with the new syntax and the ppxlib support
I'd like to write the following ( [@@js.enum] )
module String_or_number' : sig
type t =
[ `string of string [@js.default]
| `Int of int [@js.default]
]
[@@js.enum]
val t_to_js : t -> Ojs.t
val t_of_js : Ojs.t -> t
end =
[%js]
It fails with
Error: Sum types without js.* attribute not supported in this context
The way to do it is currently ([@js.enum])
module String_or_number' : sig
type t =
[ `string of string [@js.default]
| `Int of int [@js.default]
]
[@js.enum]
val t_to_js : t -> Ojs.t
val t_of_js : Ojs.t -> t
end =
[%js]
It would be nice to support the first version.
It prevents gen_js_api from being installed at the same time as ocamlformat in a 4.12 switch, since ocamlformat since 0.17 has a ocaml-migrate-parsetree >= "2.1.0" constraint.
Here's my use-case:
Electron has an app
object/module/thingy with properties, methods, and events.
I want to map those to values in an OCaml module Electron.App
because electron.app
is "static": you get it once, and work with it, you never get several of them. So no use for an OCaml-side app
type or functions that take them.
I'd rather call App.on_ready f
than any object call or a weird Electron.App.on app "ready" f
So here's how I planned things:
module App : sig
val is_packaged : bool [@@js.global "isPackaged"]
val app_menu : Menu.t option [@@js.global "applicationMenu"]
(* ... more stuff ... *)
val quit : unit -> unit [@@js.global]
val exit : ?exit_code:int -> unit -> unit [@@js.global]
end [@js.scope "electron.app"]
but the generated ml looks like
module App =
struct
let (is_packaged : bool) =
Ojs.bool_of_js (Ojs.get Ojs.global "isPackaged")
let (app_menu : Menu.t option) =
Ojs.option_of_js Menu.t_of_js (Ojs.get Ojs.global "applicationMenu")
(* ... more stuff ... *)
let (quit : unit -> unit) =
fun () ->
ignore
(Ojs.call (Ojs.get (Ojs.get Ojs.global "electron") "app") "quit"
[||])
let (exit : ?exit_code:int -> unit -> unit) =
fun ?exit_code:x4 ->
fun () ->
ignore
(let x7 = Ojs.get (Ojs.get Ojs.global "electron") "app" in
Ojs.call (Ojs.get x7 "exit") "apply"
[|x7;((let x5 = Ojs.new_obj (Ojs.get Ojs.global "Array") [||] in
(match x4 with
| Some x6 ->
ignore (Ojs.call x5 "push" [|(Ojs.int_to_js x6)|])
| None -> ());
x5))|])
end
The "properties" do not scope to electron.app
, they stay in global.
Any reason why?
I mean I can still scope them manually and put electron.app
in the [@@js.global ...]
attribute but it'd be nice to not have to do that, if I had a lot of properties to map like that
PS: I have that Menu
module defined with just a type t, and electron
is in global scope with an unsafe call to process.mainModule.require
🤷♂️
One currently cannot use ppxlib deriving together with gen_js_api.
In particular, running the code bellow will fail with Error: Cannot parse signature item
module M : sig
type t = { a : int; b : string } [@@deriving sexp]
val t_of_js : Ojs.t -> t
val t_to_js : t -> Ojs.t
end = [%js]
The reason is that ppxlib will expand deriving first and then run the gen_js_api mapper, at which point [@@deriving sexp]
has generated item that gen_js_api doesn't understand (e.g. include
).
One could imagine the following steps instead:
module M : sig
type t = { a : int; b : string } [@@deriving sexp]
val t_of_js : Ojs.t -> t
val t_to_js : t -> Ojs.t
end = struct
type t = { a : int; b : string } [@@deriving sexp]
let t_of_js = ....
let t_to_js = ....
end
module M : sig
type t = { a : int; b : string }
include sig
[@@@ocaml.warning "-333"]
val t_of_sexp : sexp -> t
val sexp_of_t : t -> sexp
end
val t_of_js : Ojs.t -> t
val t_to_js : t -> Ojs.t
end = struct
type t = { a : int; b : string }
let t_of_sexp = .....
let sexp_of_t = .....
let t_of_js = ....
let t_to_js = ....
end
Talking with @jeremiedimino, there are two approaches to explore:
Context_free.Rule.t
instead of whole AST mapper. That might require changing the syntax a littleI tried to implement a minimal "hello world" example using gen_js_api: https://github.com/Armael/gen_js_api_helloworld . However, make
fails at the last step (the js_of_ocaml
invocation) with:
js_of_ocaml --pretty -o test.js +gen_js_api/ojs_runtime.js test.byte
js_of_ocaml: Error: Unresolved internal primitive: %caml_js_opt_meth_call
make: *** [Makefile:7: all] Error 1
Am I doing something wrong? If it's a js_of_ocaml bug, it's a bit strange that people seem to manage usin gen_js_api on non-trivial examples, but I can't make it work on a trivial one...
This is either a feature request or a documentation request...
js_of_ocaml's typed_array.ml
contains the following:
module String = struct
external of_uint8Array : uint8Array Js.t -> string = "caml_string_of_array"
let of_arrayBuffer ab =
let uint8 = new%js uint8Array_fromBuffer ab in
of_uint8Array uint8
end
which is calling caml_string_of_array
from jsoo's runtime/mlString.js
to create an ocaml string backed by a javascript array.
Is there any way to do the same thing via gen_js_api?
I wrote an MLI that looked like this:
(** https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map *)
type ('a, 'b) t
val create : unit -> ('a, 'b) t [@@js.new "Map"]
val set : ('a, 'b) t -> 'a -> 'b -> unit
val get : ('a, 'b) t -> 'a -> 'b option
val delete : ('a, 'b) t -> 'a -> unit
Where the functions get
, set
, and delete
called the corresponding functions on the Map object.
Recently, after updating my version of gen_js_api, the code generation changed so that "get" and "set" were doing property accesses instead of function calls.
I found this PR #132, but there is no mention of a breaking change, nor can I find any documentation about how to get back to the original behavior.
Hi !
I have been using gen_js_api for days and it works like a charm ! However, I am encountering a problem with sum types. Indeed, I created a file try.mli containing :
type t = | Foo [@js "foo"] | Bar [@js 42] | Baz [@@js.enum]
(It's the example given in the documentation)
When I compile it (with all the commands given), the command
ocamlfind ocamlc -c -package gen_js_api.ppx try.ml
returns the error :
File "try.ml", line 4, characters 10-12: Error: Spurious js.* attribute
In fact, the .ml
file still contains the annotations [@js "foo"]
, [@js 42]
and [@@js.enum]
. If I remove then from the .ml file, then it compiles and works well. So is it normal to still have the annotations in the .ml
file ?
Thanks
Consider the following code:
module Issue142 = [%js:
type t = [`Foo [@js 42]] [@js.enum]
and u = t
]
This would generate the following implementation:
module Issue142 : sig type t = [ `Foo ]
and u = t end =
((struct
[@@@js.dummy "!! This code has been generated by gen_js_api !!"]
[@@@ocaml.warning "-7-32-39"]
type t = [ `Foo ]
and u = t
let rec t_of_js : Ojs.t -> t =
fun (x40 : Ojs.t) ->
let x41 = x40 in
match Ojs.int_of_js x41 with | 42 -> `Foo | _ -> assert false
and t_to_js : t -> Ojs.t =
fun (x39 : [ `Foo ]) -> match x39 with | `Foo -> Ojs.int_to_js 42
and u_of_js : Ojs.t -> u = t_of_js
and u_to_js : u -> Ojs.t = t_to_js
end)[@merlin.hide ])
but this fails with an error Error: This kind of expression is not allowed as right-hand side of `let rec'
because the right hand side of u_of_js
is t_of_js
, which is bound in the same group of let rec
definitions.
I've suddenly run into issues with enums -
Given a file with one line containing type t = [`foo | `bar [@js 42] | `Baz] [@@js.enum]
, when I run ocamlfind gen_js_api/gen_js_api test.mli
I get Error: Sum types without js.* attribute not supported in this context
.
I'm pretty sure this was working in the not too distant past - I'm running with ocaml 4.07.0, opam2 and gen_js_api 1.0.5 - gen_js_api is compiling without error on everything else I have.
Assume we want to define two types t
and u
with the following definitions:
[@@@js.stop]
type t = (* alias to some not JS-able type *)
val t_to_js: t -> Ojs.t
val t_of_js: Ojs.t -> t
[@@@js.start]
[@@@js.implem
type t = (* alias to some not JS-able type *)
let t_to_js x = (* some code *)
let t_of_js x = (* some code *)
]
type u =
| Unknown of Ojs.t [@js.default]
| T of t [@js "t"]
[@@js.union on_field "type"]
If t is an alias to some type that involves u
, t
and u
becomes mutually recursive. But if the said type is not supported by gen_js_api (i.e. using poly variants without js.*
attribute), things become difficult to make it compile.
Now neither of the followings work:
type 'a dummy
type u =
| Unknown of Ojs.t [@js.default]
| T of t [@js "t"]
[@@js.union on_field "type"]
and t = [`U of u] dummy
(* Error: Sum types without js.* attribute not supported in this context *)
type 'a dummy
type u =
| Unknown of Ojs.t [@js.default]
| T of t [@js "t"]
[@@js.union on_field "type"]
[@@@js.stop]
and t = [`U of u] dummy (* Error: Syntax error *)
[@@@js.start]
[@@@js.implem
and t = [`U of u] dummy (* Error: Syntax error *)
]
The best possible workaround I was able to invent is:
type 'a dummy
[@@@js.stop]
type 'u tmp = [ `U of 'u ] dummy
val tmp_to_js: ('u -> Ojs.t) -> 'u tmp -> Ojs.t
val tmp_of_js: (Ojs.t -> 'u) -> Ojs.t -> 'u tmp
[@@@js.start]
[@@@js.implem
type 'u tmp = [ `U of 'u ] dummy
let tmp_to_js _ x = Obj.magic x
let tmp_of_js _ x = Obj.magic x
]
type u =
| Unknown of Ojs.t [@js.default]
| T of t [@js "t"]
[@@js.union on_field "type"]
and t = u tmp
But this requires a bit too much of coding, loses the information of u
when writing custom to_js
/of_js
functions, and introduces an unwanted type tmp
to the scope.
Is there some way to solve this problem nicely? I think it would be great if an attribute (say js.ignore
) exists to let the type definition accepted without being processed by gen_js_api so that the following compiles:
type 'a dummy
type u =
| Unknown of Ojs.t [@js.default]
| T of t [@js "t"]
[@@js.union on_field "type"]
and t = [`U of u] dummy [@@js.ignore]
[@@@js.stop]
val t_to_js: t -> Ojs.t
val t_of_js: Ojs.t -> t
[@@@js.start]
[@@@js.implem
let t_to_js x = (* some code *)
let t_of_js x = (* some code *)
]
There are JavaScript functions that expect the value undefined
to be passed to them, not null
. gen_js_api doesn't provide (as far as I know) a way to do this, as option
types get converted to null
.
I added this to the bindings I am working on:
[@@@js.stop]
type 'a option_undefined = 'a option
[@@@js.start]
[@@@js.implem
type 'a option_undefined = 'a option
external equals: Ojs.t -> Ojs.t -> bool = "caml_js_equals"
external pure_js_expr: string -> Ojs.t = "caml_pure_js_expr"
let undefined = pure_js_expr "undefined"
let option_undefined_of_js f x =
if equals x undefined then None
else Some (f x)
let option_undefined_to_js f = function
| Some x -> f x
| None -> undefined
]
But I wonder if this type option_undefined
could be something built in as part of gen_js_api? Or maybe, as an annotation to type option
, instead of a full new built-in type.
For instance, when the first parameter of a JS constructor is a DOM element, what is the way to use the jsoo type Dom_html.element
for this parameter?
Trying something like:
class foo : div:Dom_html.element -> _foo [@@js.new "..."]
gives
Error: Unbound value Dom_html.element_to_js
Is there any example available for this kind of binding? Thanks.
It makes you need to specify my_getter [@@js.get "my_getter"]
, so why not use original case?
Edit: And I also need to specify the setter.
Currently fails with:
$ make
make -C src all
make[1]: Entering directory '/home/fabian/ocaml/not-mine/gen_js_api/src'
ocamlc -w +A-4-41-45 -warn-error +8 -c ojs.mli ojs.ml
ocamlc -w +A-4-41-45 -warn-error +8 -c ojs_exn.mli ojs_exn.ml
ocamlc -w +A-4-41-45 -warn-error +8 -a -o gen_js_api.cma ojs.cmo ojs_exn.cmo
ocamlc -w +A-4-41-45 -warn-error +8 -I +compiler-libs -o gen_js_api ocamlcommon.cma gen_js_api.mli gen_js_api.ml
File "gen_js_api.ml", line 156, characters 76-79:
156 | | Error (loc, err) -> Some (Location.error_of_printer loc print_error err)
^^^
Error: The function applied to this argument has type
?loc:Location.t -> ?sub:Location.msg list -> Location.report
This argument cannot be applied without label
When building bindings, we get these for each compiled file.
make build
dune build @install
File "_none_", line 1:
Warning 58: no cmx file was found in path for module Ojs, and its interface was not compiled with -opaque
File "_none_", line 1:
Warning 58: no cmx file was found in path for module Ojs, and its interface was not compiled with -opaque
== version info ==
<><> gen_js_api: information on all versions ><><><><><><><><><><><><><><><><><>
name gen_js_api
all-installed-versions 1.0.5
all-versions 1.0 1.0.1 1.0.2 1.0.3 1.0.4 1.0.5
<><> Version-specific details <><><><><><><><><><><><><><><><><><><><><><><><><>
version 1.0.5
repository default
pin git+ssh://[email protected]/LexiFi/gen_js_api
source-hash 7bd36e7d
When I build gen_js_api with ocaml 4.05 rc1, I get this error:
ocamlc -w +A-4-41-45 -warn-error +8 -I +compiler-libs -o gen_js_api ocamlcommon.cma gen_js_api.mli gen_js_api.ml
File "gen_js_api.ml", line 536, characters 49-60:
Error: This expression has type string Asttypes.loc = string Location.loc
but an expression was expected of type string
make[1]: *** [Makefile:14: all] Error 2
$ ocaml --version
The OCaml toplevel, version 4.05.0+rc1
The current OPAM version of gen_js_api requires ocaml-version < "4.06.0". However, the repository itself has been patched to support OCaml 4.06. An update to the OPAM repository would save 4.06 users from having to pin this repository.
I'm having trouble in trying to include modules:
type event_phase_type = Capturing_phase [@js 1] | At_target [@js 2] | Bubbling_phase [@js 3] [@@js.enum]
module Event : sig
type t = private Ojs.t
val bubbles: t -> bool
val cancelable: t -> bool
val current_target: t -> Ojs.t
val event_phase: t -> event_phase_type
val prevent_default: t -> unit
val stop_immediate_propagation: t -> unit
val stop_propagation: t -> unit
val target: t -> Ojs.t
val time_stamp: t -> float
val type_: t -> string
end
module MouseEvent : sig
include Event
type t = Event.t
val alt_key: t -> bool
val button: t -> int
val client_x: t -> int
val client_y: t -> int
val ctrl_key: t -> bool
val detail: t -> int
val meta_key: t -> bool
val related_target: t -> Ojs.t
val screen_x: t -> int
val screen_y: t -> int
val shift_key: t -> bool
val which: t -> int
end
I get a Error: Cannot parse signature item
at 'include Event'. I've played around with [%js] without a glimmer of success. Is there a solution for this?
Input (.ml
file):
include [%js:
module M: sig
type t
end
]
Generates (simplified):
include (
struct
module M = struct
type t = Ojs.t
let rec (t_of_js : Ojs.t -> t) = fun x2 -> x2
and (t_to_js : t -> Ojs.t) = fun x1 -> x1
end
end :
sig
module M : sig
type t
end
end )
One has to include the conversion functions t_of_js
and t_to_js
manually on the signature, which is quite tedious. Maybe gen_js_api could include them as well there?
Trying to define this
and can't quite work it out. With a javascript callback -
myFunction.on("mouseover", function (d) {
myObject.select(this);
})
In my gen_js_api environment I have all my functions nicely typed and so to do a select(this)
I have to perform a _to_js
on myObject and then an Ojs.call
on "select"
. Is there a more elegant way of doing this? It would be great to do something like val select: my_object_type this_type ...
.
Perhaps [@@js.call "select"]
implicitly references this
?
Hi !
Sorry if I'm missing something but, is there a way to do module Foo = Lib_foo
in .mli
files used to bind a javascript lib ?
Actually gen_js_api
throws an error, maybe it's a wanted behavior ?
Thanks
Consider the following signature:
module Date : sig get_day' : t -> int end
Using the function after deriving an implementation with gen_js_api results in an ugly runtime error. I suspect that the apostrophe finds a way into the javascript world.
Is it possible to call new on a constructor that is passed as an argument? Sometimes libraries have nested constructors that are only reachable after creating another object, and libraries loaded with "require" would not expose a global path to the constructor.
Does it mean that js.meth
is deprecated and that js.call
have to be used instead? If so, the method binding section in https://github.com/LexiFi/gen_js_api/blob/master/CLASSES.md should be updated because the code example for a method call uses js.meth
.
It's recommended that every library has a single toplevel module, and (wrapped false)
is only there to help people transition to dune. Would it be ok to break compatibility and to switch to Ojs.Exn
? If not, then perhaps we could add something to dune to help the transition from (wrapped false)
to (wrapped true)
. We already have one such mechanism (wrapped (transition "message"))
but it doesn't quite work in some cases (like this one)
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.