Showing preview only (380K chars total). Download the full file or copy to clipboard to get everything.
Repository: LexiFi/gen_js_api
Branch: master
Commit: dcdd0b1dd852
Files: 114
Total size: 352.5 KB
Directory structure:
gitextract_fdgla5ae/
├── .github/
│ ├── dependabot.yml
│ └── workflows/
│ └── workflow.yml
├── .gitignore
├── .ocp-indent
├── CHANGES.md
├── CLASSES.md
├── IMPLGEN.md
├── INSTALL_AND_USE.md
├── LICENSE
├── LOW_LEVEL_BINDING.md
├── Makefile
├── NAMING.md
├── NODE_RUNTIME_BINDINGS.md
├── PPX.md
├── README.md
├── TODO.md
├── TYPES.md
├── VALUES.md
├── dune
├── dune-project
├── examples/
│ ├── calc/
│ │ ├── calc.html
│ │ ├── calc.ml
│ │ └── dune
│ ├── misc/
│ │ ├── dune
│ │ ├── jquery.mli
│ │ ├── js_date.mli
│ │ ├── js_str.mli
│ │ ├── test_jquery.html
│ │ └── test_jquery.ml
│ └── test/
│ ├── dune
│ ├── main.html
│ ├── main.ml
│ └── test_bindings.mli
├── gen_js_api.opam
├── lib/
│ ├── dune
│ ├── ojs.ml
│ ├── ojs.mli
│ ├── ojs_exn.ml
│ ├── ojs_exn.mli
│ ├── ojs_runtime.js
│ └── ojs_runtime_stubs.c
├── node-test/
│ ├── bindings/
│ │ ├── arrays.mli
│ │ ├── buffer.mli
│ │ ├── console.mli
│ │ ├── container.ml
│ │ ├── container.mli
│ │ ├── dune
│ │ ├── errors.mli
│ │ ├── expected/
│ │ │ ├── arrays.ml
│ │ │ ├── buffer.ml
│ │ │ ├── console.ml
│ │ │ ├── errors.ml
│ │ │ ├── fs.ml
│ │ │ ├── global.ml
│ │ │ ├── imports.ml
│ │ │ ├── number.ml
│ │ │ ├── path.ml
│ │ │ ├── process.ml
│ │ │ └── promise.ml
│ │ ├── fs.mli
│ │ ├── global.mli
│ │ ├── imports.js
│ │ ├── imports.mli
│ │ ├── imports.wat
│ │ ├── number.mli
│ │ ├── path.mli
│ │ ├── process.mli
│ │ └── promise.mli
│ ├── runtime_primitives/
│ │ ├── bindings.mli
│ │ ├── dune
│ │ ├── example.ml
│ │ ├── imports.js
│ │ └── imports.wat
│ └── test1/
│ ├── dune
│ ├── recursive.js
│ ├── recursive.mli
│ └── test.ml
├── ojs.opam
├── ojs.opam.template
├── ppx-driver/
│ ├── dune
│ └── gen_js_api_ppx_driver.ml
├── ppx-lib/
│ ├── dune
│ ├── gen_js_api_ppx.ml
│ └── gen_js_api_ppx.mli
├── ppx-standalone/
│ ├── dune
│ ├── gen_js_api.ml
│ └── gen_js_api.mli
└── ppx-test/
├── binding.mli
├── binding_automatic.mli
├── binding_explicitly_automatic.mli
├── binding_manual.mli
├── dune
├── expected/
│ ├── binding.ml
│ ├── binding_automatic.ml
│ ├── extension.ml
│ ├── first_class_modules.ml
│ ├── issues.ml
│ ├── issues_mli.ml
│ ├── modules.ml
│ ├── recursive_modules.ml
│ ├── scoped.ml
│ ├── types.ml
│ └── union_and_enum.ml
├── extension.ml
├── first_class_modules.mli
├── issues.ml
├── issues_mli.mli
├── modules.mli
├── ppx/
│ ├── dune
│ └── main.ml
├── recursive_modules.mli
├── scoped.mli
├── types.ml
└── union_and_enum.mli
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/dependabot.yml
================================================
version: 2
updates:
- package-ecosystem: github-actions
directory: /
schedule:
interval: daily
================================================
FILE: .github/workflows/workflow.yml
================================================
name: Builds, tests & co
on:
- push
- pull_request
permissions: read-all
jobs:
build:
strategy:
fail-fast: false
matrix:
os:
- ubuntu-latest
- macos-latest
- windows-latest
ocaml-compiler:
- 5
- 4
include:
- os: ubuntu-latest
ocaml-compiler: "4.13"
runs-on: ${{ matrix.os }}
steps:
- name: Checkout tree
uses: actions/checkout@v6
- name: Set-up Node.js
uses: actions/setup-node@v4
with:
node-version: latest
- name: Set-up OCaml
uses: ocaml/setup-ocaml@v3
with:
ocaml-compiler: ${{ matrix.ocaml-compiler }}
- run: opam install . --deps-only --with-test
- run: opam exec -- make
- run: opam exec -- make test
# lint-doc:
# runs-on: ubuntu-latest
# steps:
# - name: Checkout tree
# uses: actions/checkout@v6
# - name: Set-up OCaml
# uses: ocaml/setup-ocaml@v3
# with:
# ocaml-compiler: 5
# - uses: ocaml/setup-ocaml/lint-doc@v3
# lint-fmt:
# runs-on: ubuntu-latest
# steps:
# - name: Checkout tree
# uses: actions/checkout@v6
# - name: Set-up OCaml
# uses: ocaml/setup-ocaml@v3
# with:
# ocaml-compiler: 5
# - uses: ocaml/setup-ocaml/lint-fmt@v3
lint-opam:
runs-on: ubuntu-latest
steps:
- name: Checkout tree
uses: actions/checkout@v6
- name: Set-up OCaml
uses: ocaml/setup-ocaml@v3
with:
ocaml-compiler: 5
- uses: ocaml/setup-ocaml/lint-opam@v3
================================================
FILE: .gitignore
================================================
gen_js_api.install
ojs.install
*.merlin
_build
_opam
.vscode
================================================
FILE: .ocp-indent
================================================
match_clause=4
strict_with=auto
================================================
FILE: CHANGES.md
================================================
Changelog
=========
Unreleased
----------
- Support for binding to js_of_ocaml runtime primitives via `@`-prefixed payloads on `[@@js.global]` and `[@@@js.scope "@..."]`, enabling generated bindings to target values supplied by the JavaScript runtime.
- Test suite updates adapted for wasm_of_ocaml.
Version 1.1.6
-------------
- GPR#181: Upgrade ppxlib dependency to 0.37.0 (=> support OCaml 5.4)
Version 1.1.5
-------------
- GPR#180: Fix for OCaml 5.3.0
Version 1.1.4
-------------
- GPR#176: Remove references to joo_global_object (@hhugo)
- GPR#175: Support for simple module construction (@sbriais)
Version 1.1.3
-------------
- GPR#173: Compatibility with Wasm_of_ocaml (@vouillon)
- GPR#171: Update build badge and remove unused travis config (@tmcgilchrist)
Version 1.1.2
-------------
- GPR#170: Make Ojs.iter_properties compatible with jsoo/effects (@vouillon)
Version 1.1.1
-------------
- GPR#167: Fix CI (@cannorin)
- GPR#166: Support first class modules to treat type variables safely (@cannorin)
Version 1.1.0
-------------
- GPR#164: Switch to js_of_ocaml.4.0 (@hhugo)
- GPR#165: Allow n-ary constructors in [@js.union] (@cannorin)
Version 1.0.9
-------------
- GPR#161: Fix broken link to VALUES.md (@joelburget)
- GPR#154: Upgrade to ocaml/setup-ocaml@v2 (@smorimoto)
- GPR#153: Support recursive modules (@cannorin)
- GPR#152: [@@js.invoke] attribute to call the global object as a function (@cannorin)
Version 1.0.8
-------------
- GPR#149: Stop using OMP directly (@mlasson)
- GPR#145: Add support for "newable" functions to [@@js.apply] (@cannorin)
- GPR#143: Disable eta reduction for of_js and to_js of type aliases (@cannorin)
- GPR#144: Disable "Spurious js.\* attribute" error for @js.dummy (@cannorin, @mlasson)
- GPR#146: Fix an edge-case bug of prepare_args
Version 1.0.7
-------------
- GPR#140: Adds a deprecation warning the automatic heuristic is used (@mlasson)
- GPR#139: Rename things for backward compatibility (@mlasson)
- GPR#135: UTF-8 support for (Ojs.get/set/delete) adaptions (@mlasson)
- GPR#132: Add support for indexers and "callable" objects (@cannorin)
- GPR#130: Javascript -> JavaScript (@smorimoto)
- GPR#129: Add GitHub Actions workflow (@smorimoto)
- GPR#128: Bucklescript -> ReScript (also add genType ppx as a resource) (@ryyppy)
- GPR#127: Support boolean "enum"s and boolean union discriminators (@cannorin)
- GPR#125: js.custom attribute for type declaration to support custom mapping #125 (@cannorin)
- GPR#123: Upgrade ppx to the ocaml 4.11 ast (@hhugo)
- GPR#120: Split runtime library to own package (@rgrinberg)
- GPR#118: Add ppx tests setup (@jchavarri, @mlasson)
- GPR#115: Support for functors and module inclusion (@mlasson)
- GPR#114: Dependency tweaks (@rgrinberg)
- GPR#113: Add support for type variables (@jchavarri, @mlasson)
- GPR#111: Better ppxlib integration (@hhugo)
- GPR#110: Include payload in extension node (@nojb)
Version 1.0.6
-------------
- GPR #101: Adds travis support + use ocaml-migrate-parsetree (@mlasson)
- GPR #94: Typo: correct wrong 'apply_arr' to 'apply' (@facelesspanda)
- GPR #89: Update the opam file (@hhugo)
- GPR #87: Switch to dune (@hhugo)
- GPR #88: Fix some warnings (@hhugo)
- GRP #85: Adapt to 4.08 (@nojb)
Version 1.0.5
-------------
- Adapt to OCaml 4.06
Version 1.0.4
-------------
- Adapt to OCaml 4.05.
================================================
FILE: CLASSES.md
================================================
Class wrapping in gen_js_api
============================
gen_js_api can bind JavaScript objects into OCaml abstract types with
associated functions (to get/set property and to call methods). This
form of binding is quite efficient, since the opaque OCaml values are
just the underlying JavaScript objects, with no mapping or wrapping.
In addition to that, gen_js_api provides ways to **wrap JavaScript
objects into OCaml objects**. This adds some runtime overhead, but
allows users to use standard OO syntax in OCaml and to rely on
inheritance (to mimic similar hierarchy on the JS side).
In addition to the runtime overhead, wrapping JS objects as OCaml
objects also forces to define all methods at once. With opaque
bindings, methods of a given JS "class" can be spread over multiple
OCaml modules. This can be especially useful to mimic the behavior of
JS library addins that extends the library's object prototype with
more methods.
Class wrapping
--------------
An interface processed by js_of_ocaml can define an OCaml class used
to wrap some JavaScript objects:
````
class my_class: Ojs.t ->
object
inherit Ojs.obj
(* method declarations *)
....
end
````
The class must inherit from `Ojs.obj` directly or indirectly. This class
simply defines a `to_js` method (returning the underlying `Ojs.t` object).
Such a class declaration produces in the implementation a class
definition with the list of `inherit` clauses (passing the `Ojs.t`
handle to each of them) and a definition for all listed methods. It
also produces a standard pair of `*_to_js`/`*_of_js` functions (the
`*_to_js` function calls the `to_js` method inherited from `Ojs.obj`,
and `*_of_js` calls the constructor of the class).
Method binding
--------------
- Property getter:
````
method foo: t
[@@js.get "foo"]
````
- Property setter:
````
method set_foo: t -> unit
[@@js.set "foo"]
````
- Method call:
````
method f: t -> unit
[@@js.call "f"]
````
As always, the names can be omitted if they correspond to the implicit
naming scheme.
Prior to version 1.0.7, as for value bindings, some implicit rules applied,
so that `[@@js.*]` attributes could often be omitted (in particular, in
all the examples above).
The following rules were applied in order:
- If the method is a function with one argument `t -> unit` and its
name starts with `set_`, then the declaration is assumed to be a
`[@@js.set]` property setter (on the property whose name is obtained
by dropping the `set_` prefix).
- If the method is a function, then the definition is assumed to be a
`[@@js.call]` method call.
- Otherwise, the method is assumed to be a `[@@js.get]` property getter.
But since version 1.0.7, *this feature has been deprecated*; all method
should be explicitly annotated or a preprocessor warning will be emitted.
Constructors
------------
The default constructor for a class wrapper is necessarily an `Ojs.t` object
(see above). (Note: it would be easy to allow such classes to take a
value of an arbitrary JS-able type, but this would make it more
difficult to support inheritance.)
It is possible to bind to actual JS constructors declarations such as:
````
class foo: string -> my_class
````
Calling this constructor is then implemented by calling the JavaScript
constructor of the same name, and wrapping the resulting object with
the `my_class` wrapper. This is similar to defining:
````
val foo: string -> my_class
[@@js.new]
````
but allows writing `new foo(...)` instead of `foo(...)`.
A custom name can be provided with a `[@@js.new]` attribute:
````
class foo: string -> my_class
[@@js.new "MyConstr"]
````
================================================
FILE: IMPLGEN.md
================================================
gen_js_api: generate implementations from interfaces
====================================================
The primary operating mode for gen_js_api is to generate .ml
implementation from annotated .mli interfaces. These interfaces must
follow a certain shape. They describe both the JavaScript components
to be imported and how they should be reflected within OCaml.
Usage
-----
```
$ gen_js_api my_module.mli
```
or with findlib:
```
$ ocamlfind gen_js_api/gen_js_api my_module.mli
```
This generates my_module.ml.
Supported declarations
----------------------
Interfaces processed by gen_js_api can currently contain:
- [Type declarations](TYPES.md):
````
type t = ...
````
See [this page](TYPES.md) for a description of supported types.
Such a type declaration produces in the implementation an identical
defininition, and associated `*_to_js` and `*_of_js` functions
(which can be manually exported if needed).
- [Value declarations](VALUES.md):
````
val f: tyexpr
````
This produces in the implementation a definition for such a value,
whose content depends on three elements: the name of the value
(`f` in the example), its declared type (`tyexpr`), and possible
`[@@js.xxx]` attributes attached to the declaration in the interface.
See [this page](VALUES.md) for supported forms of value declarations.
- Sub-modules:
````
module M : sig
...
end
````
This naturally produces in the implementation a corresponding sub-module:
````
module M = struct
...
end
````
- Module aliases:
If a module alias is declared in the interface, like:
```ocaml
module M = <Module Path>
```
it is directly reflected in the generated implementation without modifications.
- Module inclusion:
To include a module `M` in the generated implementation, simply add
```ocaml
include (module type of M)
```
in the corresponding interface.
- [Class declarations](CLASSES.md)
Verbatim sections
-----------------
A floating attribute `[@@@js.stop]` tells the tool to ignore the
remaining items until the end of the current (possibly nested)
signature. This can be reverted with a floating attribute
`[@@@js.start]`. This system makes it possible to specify fragments
of the interface that should not generate any code in the
implementation.
A floating `[@@@js.implem ...]` tells the tool to generate some custom
code in the implementation. The payload `...` is an OCaml structure,
which is processed in the same way as in [ppx mode](PPX.md).
Example:
```ocaml
[@@@js.stop]
val foo: int -> unit
[@@@js.start]
[@@@js.implem
val foo_internal: string -> int -> unit
[@@js.global "foo"]
let foo = foo_internal ""
]
```
For the common case where verbatim sections are used to create custom
value bindings, a `[@@js.custom]` attribute can be applied to a `val`
declaration. The effect is that the `val` declaration itself is ignored
(nothing is generated in the implementation), and a structure can be
provided as the payload of the attribute. The example above is equivalent
to:
```ocaml
val foo: int -> int
[@@js.custom
val foo_internal: string -> int -> unit
[@@js.global "foo"]
let foo = foo_internal ""
]
```
and to:
```ocaml
val foo: int -> int
[@@js.custom]
[@@js.implem
...
]
```
================================================
FILE: INSTALL_AND_USE.md
================================================
gen_js_api: installation and usage instructions
===============================================
Dependencies
------------
gen_js_api does not have any external build-time dependency except
the OCaml compiler (version 4.03). Of course, it will be used
in conjuncion with the js_of_ocaml compiler and runtime support.
Installation (with OPAM)
------------------------
````
opam install gen_js_api
````
Or, to track the development version:
````
opam pin add gen_js_api https://github.com/LexiFi/gen_js_api.git
````
Manual installation
-------------------
````
git clone https://github.com/LexiFi/gen_js_api.git
cd gen_js_api
make all
make install # assuming opam-installer is installed
````
Usage (with dune)
-----------------
- Invoking the [standalone tool](IMPLGEN.md) (`.mli` -> `.ml` generator):
```
(rule
(targets my_unit.ml)
(deps my_unit.mli)
(action (run %{bin:gen_js_api} %{deps})))
```
- Compiling binding (`.mli` and generated `.ml` files), user
code which rely on the `Ojs` or with the [ppx processor](PPX.md):
```
(executables
(names test_jquery)
(js_of_ocaml)
(libraries ojs js_of_ocaml)
(preprocess (pps gen_js_api.ppx))
(modes byte)
)
```
- Compiling into JavaScript: Just ask dune to build the `*.bc.js`
target. (e.g. `dune build test_jquery.bc.js`)
Usage (with ocamlfind)
----------------------
- Invoking the [standalone tool](IMPLGEN.md) (`.mli` -> `.ml` generator):
```
ocamlfind gen_js_api/gen_js_api my_unit.mli
```
- Compiling binding (`.mli` and generated `.ml` files) and user
code which rely on the `Ojs` module:
```
ocamlfind ocamlc -package gen_js_api my_unit.mli
ocamlfind ocamlc -package gen_js_api my_unit.ml
```
- Compiling with the [ppx processor](PPX.md):
```
ocamlfind ocamlc -c -package gen_js_api my_prog.ml
```
- Linking the bytecode program:
```
ocamlfind ocamlc -o my_prog -package gen_js_api -linkpkg ...
```
- Compiling into JavaScript:
```
js_of_ocaml -o my_prog.js +gen_js_api/ojs_runtime.js my_prog
```
================================================
FILE: LICENSE
================================================
The MIT License (MIT)
Copyright 2015 by LexiFi.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
================================================
FILE: LOW_LEVEL_BINDING.md
================================================
gen_js_api: low-level binding to JavaScript
===========================================
The code generated by gen_js_api relies on a `Ojs` module (the runtime
support library). In the same way that OCaml `Obj` module exposes
(unsafe) operations to manipulate arbitrary OCaml values (after
casting them to a universal type `Obj.t`), `Ojs` allows to manipulate
arbitrary JavaScript values through an `Ojs.t` universal type.
`Ojs` encourages to think of native JS values as being "foreign"
values, even though in practice, all OCaml values are represented as
JS values when the OCaml code is compiled with js_of_ocaml. In
particular, `Ojs` does not expose a function allowing to cast an
arbitrary OCaml value to `Ojs.t` (this can always be done with
`Obj.magic`).
`Ojs.t` is similar to `Js.Unsafe.any` type, but it abstracts away from
specific properties of how js_of_ocaml represents OCaml values. For
instance the fact, that OCaml integers are encoded directly as JS
numbers is not apparent in `Ojs`, and if this property was to change,
client code would be unaffected.
Abstracting away from js_of_ocaml encoding would also make it easy to
change the way OCaml and JS are connected (either because of changes
in js_of_ocaml's encoding of OCaml values, or because an entirely
different technology is used, such as an OCaml bytecode interpreter
written in JavaScript or a JavaScript engine linked with native OCaml
code).
Note that code generated by gen_js_api doesn't depend on js_of_ocaml's
standard library (`Js` module), only on js_of_ocaml's runtime system.
Our local `Ojs` interface maps directly to primitives provided by
js_of_ocaml's runtime.
Users of gen_js_api would not use `Ojs` very often, except to define
"opaque sub-types" of `Ojs.t` in order to represent JS "classes" or
"interfaces":
```ocaml
type t = private Ojs.t
```
Occasionnaly, it it useful to go down to `Ojs` in order to define
**custom mappings** between JS and OCaml. For instance, one can
define a type for association lists indexed on strings in OCaml that
are mapped to JS objects:
```ocaml
module Dict : sig
type 'a t = (string * 'a) list
val t_to_js: ('a -> Ojs.t) -> 'a t -> Ojs.t
val t_of_js: (Ojs.t -> 'a) -> Ojs.t -> 'a t
end = struct
type 'a t = (string * 'a) list
let t_to_js ml2js l =
let o = Ojs.empty_obj () in
List.iter (fun (k, v) -> Ojs.set o k (ml2js v)) l;
o
let t_of_js js2ml o =
let l = ref [] in
Ojs.iter_properties o
(fun k -> l := (k, js2ml (Ojs.get o k)) :: !l);
!l
end
```
================================================
FILE: Makefile
================================================
# The package gen_js_api is released under the terms of an MIT-like license.
# See the attached LICENSE file.
# Copyright 2015 by LexiFi.
.PHONY: all examples test test-promote clean install uninstall doc reindent publish
all:
dune build @install @DEFAULT
examples:
dune build @examples/DEFAULT
doc:
dune build @doc
test:
dune build @runtest
test-promote:
dune build @runtest --auto-promote
clean:
dune clean
PREFIX := $$(opam config var prefix)
install:
opam-installer --prefix $(PREFIX) gen_js_api.install
uninstall:
opam-installer -u --prefix $(PREFIX) gen_js_api.install
reindent:
git ls-files *.ml *.mli | grep -v expected | xargs ocp-indent -i
VERSION := $$(opam show . | grep "^version" | sort -u | sed 's/version *//')
publish: all
echo "Publishing v$(VERSION) ..."
git tag -a v$(VERSION)
git push origin v$(VERSION)
opam publish
================================================
FILE: NAMING.md
================================================
gen_js_api: default naming convention
=====================================
JavaScript names corresponding to bound components can always be
specified explicitly (with the use of attributes). When the naming is
left implicit, a JavaScript name is automatically derived from the
OCaml name by applying the following rules:
1. uppercasing every character following an underscore;
2. removing every underscore;
3. uppercasing the first character when generating object constructor names.
This automatic naming convention can be partially disabled by adding
an attribute `[@js.verbatim_names]` on outer structures. When the attribute
`[@js.verbatim_names]` is inherited from the context, the rule 1 and 2 are
disabled.
For instance,
```ocaml
type myType = { x_coord : int; y_coord : int [@js "Y"]}
```
is mapped to a JS record with two fields named "xCoord" and "Y" whereas
```ocaml
type myType = { x_coord : int; y_coord : int [@js "Y"]} [@@js.verbatim_names]
```
is mapped to a JS record with two fields named "x_coord" and "y".
================================================
FILE: NODE_RUNTIME_BINDINGS.md
================================================
# Binding Node.js Modules with Runtime Primitives
This guide shows how to use the new runtime primitive support in `gen_js_api` to bind Node.js libraries that are usually obtained with `require(...)`. The feature hinges on two additions:
- any `[@@js.global "@primitive_name"]` binding returns an `Ojs.t` pointing to a primitive exported by the JavaScript runtime;
- a scope string that starts with `@` (for example `[@@@js.scope "@node_fs.promises"]`) resolves the first path component through the runtime primitives before following regular properties.
Together, those tools let you keep your bindings declarative while delegating the actual `require` calls to a tiny JavaScript stub.
## Example layout
```
runtime_primitives/
dune
imports.js
imports.wat
bindings.mli
example.ml
```
### Step 1 - expose the runtime primitives
Create a JavaScript file that `require`s the Node modules you need and publishes them as js_of_ocaml runtime primitives. The js_of_ocaml linker recognises `//Provides: <name>` comments and registers the value under that name at startup.
```javascript
// runtime_primitives/imports.js
'use strict';
//Provides: node_path
var node_path = require('path');
//Provides: node_fs
var node_fs = require('fs');
//Provides: node_version
var node_version = require('process').version;
//Provides: node_console
var node_console = console.log;
```
When targeting WebAssembly you also need to expose the primitives through a `.wat` shim so that `wasm_of_ocaml` can import them at runtime:
```wat
;; runtime_primitives/imports.wat
(global (export "_node_path") (import "js" "node_path") anyref)
(global (export "_node_fs") (import "js" "node_fs") anyref)
(global (export "_node_version") (import "js" "node_version") anyref)
(global (export "_node_console") (import "js" "node_console") anyref)
```
List this file in your dune stanza so that js_of_ocaml ships it with the compiled artefacts:
```
; runtime_primitives/dune
(rule
(targets bindings.ml)
(deps bindings.mli)
(action (run gen_js_api %{deps})))
(executable
(name example)
(libraries ojs)
(preprocess (pps gen_js_api.ppx))
(modes js wasm)
(js_of_ocaml (javascript_files imports.js))
(wasm_of_ocaml (javascript_files imports.js imports.wat)))
```
Adding the file to both `js_of_ocaml` and `wasm_of_ocaml` makes the primitives available in browser and wasm builds alike.
### Step 2 - bind module functions with `[@js.scope "@..."]`
Use `module [@js.scope "@primitive"]` blocks to call methods on runtime primitives without manually threading the module objects. The interface below covers the synchronous filesystem API used in the reference JavaScript while keeping the underlying modules abstract.
```ocaml
(* runtime_primitives/bindings.mli *)
module [@js.scope "@node_fs"] Fs : sig
val write_file_sync : string -> string -> unit [@@js.global "writeFileSync"]
val read_file_sync : string -> encoding:string -> string [@@js.global "readFileSync"]
val readdir_sync : string -> string array [@@js.global "readdirSync"]
val append_file_sync : string -> string -> unit [@@js.global "appendFileSync"]
end
module [@js.scope "@node_path"] Path : sig
val separator: string [@@js.global "sep"]
val join : (string list [@js.variadic]) -> string [@@js.global "join"]
end
```
Each module-level scope starts with `@`, so the ppx turns calls like `Fs.write_file_sync` into direct invocations on the corresponding Node module (`node_fs.writeFileSync` in this case) without requiring you to pass the module object around.
### Step 3 - bind direct values with `@`-prefixed `[@@js.global]`
When you only need the primitive itself—such as a constant exported by a Node module—use the `@` prefix inside `[@@js.global]` to obtain it directly as an OCaml value.
```ocaml
(* runtime_primitives/primitives_bindings.mli continued *)
val node_version : string [@@js.global "@node_version"]
val log : string -> unit [@@js.global "@node_console"]
```
These expand to `Jsoo_runtime.Js.runtime_value ...` calls and convert the results to the requested OCaml types, so you can expose constants or functions alongside the scoped modules described above.
### Step 4 - port the JavaScript example
`main.ml` mirrors the original JavaScript snippet that writes, reads, appends, and re-reads a file while logging progress to the Node console. It relies on the scoped `Fs`/`Path` modules plus the direct `log`, `path_separator`, and `node_version` values.
```ocaml
open Bindings
let initial_content = "Hello, Node.js!"
let appended_line = "\nAppending a new line."
let encoding = "utf-8"
let filename = "example.txt"
let run () =
let file = Path.join ["."; filename] in
Fs.write_file_sync file initial_content;
let content = Fs.read_file_sync file ~encoding in
if content <> initial_content then
failwith "Unexpected initial content";
log ("File content: " ^ content);
let files = Fs.readdir_sync "." |> Array.to_list in
if not (List.mem filename files) then
failwith "example.txt missing from directory listing";
log ("Files in current directory: " ^ String.concat ", " files);
Fs.append_file_sync file appended_line;
let updated = Fs.read_file_sync file ~encoding in
if updated <> initial_content ^ appended_line then
failwith "Append failed";
log ("Updated content: " ^ updated);
log ("Path separator reported by Node: " ^ Path.separator);
log ("Node.js version: " ^ node_version)
let () = run ()
```
### Putting it together
1. Declare each required Node module once in `imports.js` (and mirror them in `imports.wat` for wasm) using the js_of_ocaml `//Provides:` convention.
2. Export the files through dune so that the js_of_ocaml toolchain registers those primitives at runtime.
3. Map node modules in OCaml with `module [@js.scope "@primitive"]` blocks, and use `@`-prefixed `[@@js.global]` bindings for direct values.
4. Consume the generated modules from OCaml exactly as you would in JavaScript, as shown in `example.ml`.
With these pieces in place you can keep writing high-level `gen_js_api` bindings while relying on the new runtime primitive support to bridge your OCaml code to Node-specific libraries provided via `require`.
================================================
FILE: PPX.md
================================================
gen_js_api: ppx mode
====================
While the primary mode of operation for gen_js_api is to generate an
.ml file from an annotated .mli file, it is also possible to use it as
a ppx preprocessor on an .ml file directly to insert local JS bindings.
The `-ppx` command-line option must be the first argument passed
to gen_js_api to enable the ppx mode:
```
$ ocamlc -c -ppx "gen_js_api -ppx" my_prog.ml
```
or with findlib:
```
$ ocamlfind ocamlc -c -package gen_js_api my_prog.ml
```
Note: the ppx currently does nothing on `.mli` files.
Several forms are supported:
- `[%js: <signature>]` extension as a module expression. Examples:
````
include [%js: <signature>]
module M = [%js: <signature>]
````
The signature is processed as if it were found in an .mli file, and
the resulting structure is inserted in place of the `[%js: ...]`
extension. See [this page](IMPLGEN.md) for a list
of declarations supported in such interfaces.
- `[@@js]` attributes on type declarations.
Example:
````
type t = { x : int; y : int } [@@js]
````
This generates the corresponding `*_of_js` and `*_to_js` functions.
In case of a multi-type declaration, each type must be annotated
with `[@@js]` (if needed). See [this page](TYPES.md) for a description
of support forms of type declarations.
- `[%js.to: ty]` and `[%js.of: ty]` extensions on expressions.
Example:
````
let x : Ojs.t = [%js.of: int list] [ 10; 20; 30 ]
````
This form generates the mapping function associated to a JS-able type.
See [this page](TYPES.md) for a description of JS-able type.
================================================
FILE: README.md
================================================
gen_js_api: easy OCaml bindings for JavaScript libraries
========================================================
[](https://github.com/LexiFi/gen_js_api/actions/workflows/workflow.yml)
Overview
--------
gen_js_api aims at simplifying the creation of OCaml bindings for
JavaScript libraries. It must currently be used with the [js_of_ocaml
compiler](https://github.com/ocsigen/js_of_ocaml), although other ways
to run OCaml code "against" JavaScript might be supported later with
the same binding definitions (for instance,
[Bucklescript](https://github.com/bloomberg/bucklescript),
or direct embedding of a JS engine in a native OCaml application).
gen_js_api is based on the following ideas:
- Authors of bindings write OCaml signatures for JavaScript libraries
and the tool generates the actual binding code with a combination
of implicit conventions and explicit annotations.
- The generated binding code takes care of translating values between
OCaml and JavaScript and of dealing with JavaScript calling
conventions.
- All syntactic processing is done by authors of bindings: the client
code is normal OCaml code and does not depend on custom syntax nor
on JS-specific types.
gen_js_api can be used in two complementary ways:
- [Generating .ml implementations from annotated .mli interfaces](IMPLGEN.md),
in order to create the code for stub libraries.
- As a [ppx preprocessor on implementations](PPX.md) to define local
bindings.
Examples
--------
The repository contains some examples of OCaml bindings to JavaScript
libraries created with gen_js_api:
- Very partial [bindings to jQuery](examples/misc/jquery.mli), with
some [example client code](examples/misc/test_jquery.ml).
- Partial bindings to JavaScript [strings and
regexps](examples/misc/js_str.mli) and JavaScript
[dates](examples/js_date.mli).
- Some [ad hoc test](examples/test) to exercise various features.
- An example of a self-contained program, a [simple
calculator](examples/calc/calc.ml), implementing local .bindings
Documentation
-------------
- [Install and use](INSTALL_AND_USE.md)
- [Low-level binding to JavaScript](LOW_LEVEL_BINDING.md)
- [Using gen_js_api to generate .ml from .mli](IMPLGEN.md)
- [Using gen_js_api as a ppx processor](PPX.md)
- [Default naming convention](NAMING.md)
- [JS-able types and type declarations](TYPES.md)
- [Value bindings](VALUES.md)
- [Class-wrapping bindings](CLASSES.md)
- [TODO list](TODO.md)
Related projects
----------------
- [js_of_ocaml](https://github.com/ocsigen/js_of_ocaml): The compiler
and runtime system on which gen_js_api relies. (Note: gen_js_api
doesn't depend on js_of_ocaml's OCaml library, nor on its language
extension.)
- [goji](https://github.com/klakplok/goji): A DSL to describe OCaml
bindings for JavaScript libraries.
- [DefinitelyMaybeTyped](https://github.com/andrewray/DefinitelyMaybeTyped):
A project to parse
[DefinitelyTyped](https://github.com/borisyankov/DefinitelyTyped)
interfaces and produce OCaml interfaces.
- [ReScript](https://github.com/rescript-lang/rescript-compiler):
Another compiler from OCaml to JavaScript, featuring the [genType](https://github.com/reason-association/genType) ppx for generating TS / Flow types and runtime converters.
About
-----
gen_js_api has been created by LexiFi for porting a web application
from JavaScript to OCaml. The tool has been used in production since
2015.
This gen_js_api package is licensed by LexiFi under the terms of the
MIT license.
See see [Changelog](CHANGES.md)
Contact: alain.frisch@lexifi.com
Contributors:
- Alain Frisch
- Sebastien Briais
================================================
FILE: TODO.md
================================================
TODO list for gen_js_api
========================
- Create reasonably complete bindings for JavaScript's stdlib
(string, regexp), for the DOM, for jQuery, etc.
- Add a safe mode, where the generated code is augmented with explicit
checks (e.g. when casting a JS value to a string or integer, when
accessing a property, etc).
- Optimize generated code (for instance, lift calls to string_of_js on
literals).
- Idea: to facilitate binding and calling multiple methods at once,
provide something like (jQuery example):
```ocaml
val set: ?text:string -> ?hide:unit -> ?css:(string * string) -> t -> unit
[@@js.multicall]
```
One can then write:
```ocaml
set
~text:"Hello"
~hide:()
node
```
Each provided argument yields one method call (in the order where
arguments are declared, of course). This is mostly interesting when
methods are used to "set" internal properties, and when the different
calls commute.
This could be simulated with:
```ocaml
val set: ?text:string -> ?hide:unit -> ?css:(string * string) -> t -> unit
[@@@js.custom
val set_text: t -> string -> unit
[@@js.meth "text"]
let set ?text ... x =
Option.iter (set_text x) text;
...
]
```
- Optional arguments on JS methods are usually at the end. But this
forces to add a `unit` pseudo-argument. One could have an
(optional) convention to push optional arguments at the end of the JS
call even though there are not in the OCaml type. This would also
work for instance methods:
```ocaml
val foo: ?bla:int -> t -> int
```
instead of:
```ocaml
val foo: t -> ?bla:int -> unit -> int
```
- When defining a binding to a function with `[@@js.global
"foo.bar"]`, this is currently interpreted as calling this global
function. One could interpret it as calling the bar method on
object foo, which would have the effect of assigning `this` during
the function evaluation.
================================================
FILE: TYPES.md
================================================
Types supported in gen_js_api
=============================
JS-able types
-------------
A JS-able type is an OCaml type whose values can be mapped to and from
JavaScript objects.
The following types are supported out-of-the-box:
- Basic built-in types: `string`, `int`, `bool`, `float` and `Ojs.t`.
- Tuples of JS-able types (mapped to JS arrays).
- Sequences of JS-able types: `array` and `list`, both mapped to JS
arrays (which are assumed to be indexed by integers 0..length-1).
- Options on JS-able types. They are mapped to the same type as
their parameter: `None` is mapped to JS `null` value, and both
`null` and `undefined` are mapped back to `None`. This encoding
doesn't support nested options in a faithful way.
- Arrows (see section below).
- Polymorphic variants with only constant variants are supported
(see the section on enums below).
- Polymorphic variants can also be used to encode non-discriminated
unions on the JS side (see the section on union types below).
- Polymorphic variants can also be used to encode discriminated
unions on the JS side (see the section on discriminated union types
below).
- Free type variables like `'a`, they will involve no runtime
mapping when moving between OCaml and JS (see type variable section).
An arbitrary non-parametrized type with path `M.t` is JS-able if the
following two values are available in module `M`:
```ocaml
val t_to_js: t -> Ojs.t
val t_of_js: Ojs.t -> t
```
The name of these values is obtained by appending `_of_js` or `_to_js`
to the local name of the type. It is thus possible to define JS-able
manually by defining these two functions. Type and class declarations
processed by gen_js_api (see sections below) create JS-able type (by
generating those functions automatically).
Parametrized types can also be JS-able. It is currently assumed that
such types are covariant in each of their parameter. Mapping
functions take extra arguments corresponding to the mapper for each
parameter. For instance, a type `'a t` would need to come with the following
functions:
```ocaml
val t_to_js: ('a -> Ojs.t) -> 'a t -> Ojs.t
val t_of_js: (Ojs.t -> 'a) -> Ojs.t -> 'a t
```
Arrow types
-----------
Arrow types can also be used in contexts that expect JS-able types.
All arguments must be JS-able types, and a final `unit`
pseudo-argument is allowed (and mandatory when there is no real
argument). The function's result can be either a JS-able type or
`unit`. Note that `unit` is not considered as a proper JS-able type:
it is only allowed in these two contexts (as the result, or the final
pseudo-argument).
Arguments can be **labelled or optional**. Labels are simply ignored
on the JS side. Optional arguments have different treatments:
- When mapping an OCaml function to JS (e.g. a callback), optional
arguments are treated as normal values of an option type (i.e.
both `null` and `undefined` are mapped to `None`).
- When mapping a JS function to OCaml, it is possible to specify
a default value to fill in a missing argument:
```ocaml
val f: t -> ?x:(int [@js.default 0]) -> unit -> t
```
If no default value is provided and the argument is missing, the
argument is *dropped* from the list of arguments passed to the JS
call (this apply to function/method/constructor calls).
- In `[@@js.builder]` values, missing optional arguments are ignored
(they don't create any property on the object).
There is a special treatment for optional argument on a
`[@js.variadic]` argument (see below), in which case a missing value
is interpreted as an empty list (i.e. no extra arguments).
When mapping an OCaml function to JS, the **function arity** is the
number of real arguments (not counting the final `unit`) and the
semantics is the standard one for JS functions: missing arguments are
filled with `undefined` and extra arguments are dropped. The correct
way to support a calling convention where the JS caller might not
provide all arguments to a function defined in OCaml is to use
optional arguments (or just arguments with option types) on the OCaml
side.
In order to define **functions that return functions**, one can put a
`[@js.dummy]` attribute (or any arbitrary attribute) on the resulting type :
```ocaml
t1 -> (t2 -> t3 [@js.dummy])
```
Without the attribute, such a type would be parsed as a function of
arity 2 (returning type `t3`).
**Variadic functions** are supported, by adding a `[@js.variadic]`
attribute on the last parameter (which will represent all remaining
arguments):
```ocaml
val sep: string -> (string list [@js.variadic]) -> string
```
Type declarations
-----------------
All type declarations processed by gen_js_api create JS-able types,
i.e. associated `*_to_js` and `*_to_js` mapping functions. A
optional "private" modifier is allowed on the type declaration (in the
interface) and dropped from the generated definition (in the
implementation). Mutually recursive type declarations are supported.
- "Abstract" subtype of `Ojs.t`:
```ocaml
type t = private Ojs.t
```
This is used to bind to JS "opaque" objects, with no runtime mapping
involved when moving between OCaml and JS (mapping functions are the
identity).
- Abstract type
```ocaml
type t
```
This will generate `type t = Ojs.t` in the implementation. This
is very similar to the case above.
- Type abbreviation:
```ocaml
type t = tyexp
```
(formally, abstract types with a manifest). This assumes that the
abbreviated type expression is itself JS-able. Note that the first
kind of type declaration above (abstract subtypes of `Ojs.t`) are
a special kind of such declaration, since `abstract` is always dropped
and `Ojs.t` is JS-able.
- Record declaration:
```ocaml
type t = { .... }
```
This assumes that the type for all fields are JS-able. Fields can
be mutable (but conversions still create copies).
Polymorphic fields are not yet supported.
OCaml record values of this type are mapped to JS objects (one
property per field). By default, property names are equal to OCaml
labels, but this can be changed manually with a `[@js]` attribute.
```ocaml
type myType = { x : int; y : int [@js "Y"]}
```
- Parametrized Type:
It is allowed to parametrize types processed by gen_js_api as long as
type variables does not occur at contravariant positions.
For instance :
```ocaml
type ('a, 'b) coord = { x : 'a; y : 'b}
```
is accepted while :
```ocaml
type 'a t = 'a -> int
```
is rejected.
- Sum type declaration, mapped to enums (see Enums section).
- Sum type declaration with non constant constructors, mapped to records with a discriminator field (see Sum types section).
- Arbitrary type with custom mappings
If you want to use a type that is not supported by gen_js_api, you can make it JS-able by providing
your own `*_of_js` and `*_to_js` functions (custom mappings) with a `[@@js.custom ...]` attribute.
```ocaml
type t = ... [@@js.custom
{
of_js = (fun ... -> ...);
to_js = (fun ... -> ...)
}
]
```
This is particularly useful when the type is mutually recursive with other types which can be processed by gen_js_api.
See the [section on manually created bindings](LOW_LEVEL_BINDING.md) for more information on writing custom mappings by hand.
Not to be confused with [the `[@@js.custom]` attribute for `val` declarations](IMPLGEN.md#verbatim-sections).
Enums mapped to polymorphic variants or sum types
-------------------------------------------------
Either polymorphic variants or normal sum types (all with constant
constructors) can be used to bind to "enums" in JavaScript. By
default, constructors are mapped to the JS string equal to their OCaml
name, but a custom translation can be provided with a `[@js]`
attribute. This custom translation can be a string or an integer
literal or a float literal.
```ocaml
type t =
| Foo [@js "foo"]
| Bar [@js 42]
| Baz [@js 4.2]
| Qux
[@@js.enum]
type t = [`foo | `bar [@js 42] | `baz [@js 4.2] | `Qux] [@@js.enum]
```
It is possible to specify constructors with one argument of
type (int or float or string), used to represent "all other cases" of JS values.
```ocaml
type status =
| OK [@js 1]
| KO [@js 2]
| OO [@js 1.5]
| OtherS of string [@js.default]
| OtherI of int [@js.default]
[@@js.enum]
```
There cannot be two default constructors with the same argument type.
Also, there cannot be default constructors of type int and float at the same time.
Sum types mapped to records with a discriminator field
------------------------------------------------------
Either polymorphic variants or sum types can be mapped to JS records
with a discriminator field.
By default, the name of the discriminator field is `kind`, but this
can be changed by specifying a field name as attribute value of the
`[@@js.sum]` attribute. The value of the discriminator field is set to
the representation of the constructor name: it is derived
automatically from the constructor name but can also be specified with
a `[@js]` attribute. In this latter case, it can be either a string or
an integer.
A constant constructor is simply mapped to a record containing the
discriminator field.
A unary constructor is mapped to a record containing two fields: the
discriminator field and an argument field representing the unique
argument of the constructor. The argument field name is by default
`arg`, but this can be changed with a `[@js.arg]` attribute.
At most one unary constructor may have the attribute `[@js.default]`
and the argument of this constructor must be of type `Ojs.t`. In this
case, this constructor is used to handle the default case when either
the discriminator field is equal to an unexpected value or even worse
when the discriminator field is absent (from JS to ML). In the other
direction (from ML to JS), the unique argument is used as JavaScript
representation.
A nary constructor is mapped to a record containing two fields: the
discriminator field and an argument field set to an array representing
the arguments of the constructor. Once again, the argument field name
is by default `arg`, but this can be changed with a `[@js.arg]`
attribute. In the case of polymorphic variant, if the argument is a
tuple, then the polymorphic variant constructor is considered to be
n-ary.
Finally, an inline record constructor is mapped to a record containing
all the field of the record in addition of the discriminator
field. The name of the fields are derived from the name of the record
fields. As usual, these names can be customized using a `[@js]`
directive. This last case only applies to sum types.
```ocaml
type t =
| A
| B of int
| C of int * string
| D of {age: int; name: string}
| Unknown of Ojs.t [@js.default]
[@@js.sum]
```
The following declaration is equivalent to the previous one.
```ocaml
type t =
| A [@js "A"]
| B of int [@js.arg "arg"]
| C of int * string [@js.arg "arg"]
| D of {age: int [@js "age"]; name: string}
| Unknown of Ojs.t [@js.default]
[@@js.sum "kind"]
```
Union types
-----------
It is common for JS functions to allow arguments of several different
types (for instance, a string or an object). To represent this calling
convention, one can use polymorphic variants:
```
val f: t -> ([`Str of string | `Obj of t | `Nothing] [@js.union]) -> ...
```
When the `[@js.union]` attribute is used without any other option,
only the ML to JS function is generated. The ML to JS conversion
function simply maps constant constructors to the `null` value,
unary constructors to the value of the constructor argument,
and n-ary constructors to the array of the constructor argument values
(i.e. treated as a tuple).
For generating the converse function, one needs to have a way to
distinguish JS values in the union type. At the moment, union types
with a discriminator field argument are supported. To indicate the
name of the field, one can add extra option `on_field "kind"` (where
"kind" is the name of the field) to the `[@js.union]` attribute. In
this case, the JS to ML conversion function will inspect the value of
the field named "kind" and will map the JS value to the corresponding
unary constructor. As for sum types, the value of the discriminator
field is deduced from the name of the constructors but it can always
be overridden by using a `[@js]`attribute.
```
type close_path
type moveto_abs
type svg_path_seg =
| Unknown of Ojs.t [@js.default]
| Close_path of close_path [@js 1]
| Moveto_abs of moveto_abs [@js 2]
[@@js.union on_field "pathSegType"]
```
As for sum types, at most one unary constructor may have the
`[@js.default]` attribute and the argument of this constructor must be
of type `Ojs.t`. In this case, this constructor is used to handle the
default case when either the discriminator field is equal to an
unexpected value or even worse when the discriminator field is absent
(from JS to ML).
Discriminated union types
-------------------------
It is common for JS functions to allow arguments of several different
types (for instance, a string or an object), whose type depends on a
preceding argument. To represent this calling convention, one can use
polymorphic variants:
```
val f: t -> ([`Str of string | `Obj of t | `Nothing] [@js.enum]) -> ...
```
This generalisation of the `[@js.enum]` attribute can only be used on
polymorphic variant used in contravariant context (i.e. to describe
mapping from OCaml to JavaScript, not the other way around). With
this calling convention, first the representation of the constructor
(which can be an integer or a float or a string, which is derived
automatically if not specified with a `[@js]` attribute) is passed,
followed by the n arguments of the constructor.
Type variables
--------------
Unbound type variable are processed implicitly coerced from and to
`Ojs.t` using unsafe coercion.
This is useful when writing bindings to JS functions that rely on data structures
that can contain OCaml values as is. For example, to directly use the JS
arrays to store OCaml values in their original runtime representation, a
`JsArray` module could be defined:
```ocaml
module JsArray : sig
type 'a t = private Ojs.t
val t_of_js: (Ojs.t -> 'a) -> Ojs.t -> 'a t
val t_to_js: ('a -> Ojs.t) -> 'a t -> Ojs.t
val create: int -> 'a t [@@js.new "Array"]
val push: 'a t -> 'a -> unit [@@js.call]
val pop: 'a t -> 'a option [@@js.call]
end
```
**Important:** the functions generated from types with variables will only apply
the identity function when converting to or from JS. So this approach should
never be used to interface with a JS function that expects the types to be
converted. For example, the following would break if we used it as a binding
to the [`Array.join`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/join)
function:
```ocaml
val join: string JsArray.t -> string -> string
```
Indeed, the objects contained in the JsArray.t are not JavaScript strings but
representation of caml strings.
To properly do this, we would want the strings contained in the data structure
to be converted /to JS types, this would require conversion functions not
ignoring their first argument that are manually implemented.
One approach is to use functors instead:
```ocaml
(* Ojs.T is defined as follows:
module type T = sig
type t
val t_to_js : t -> Ojs.t
val t_of_js : Ojs.t -> t
end
*)
module JsArray (E: Ojs.T): sig
type t
val t_to_js: t -> Ojs.t
val t_of_js: Ojs.t -> t
val create: unit -> t [@@js.new "Array"]
val push: t -> E.t -> unit [@@js.call]
val pop: t -> E.t option [@@js.call]
end
module StringArray : sig
include (module type of JsArray(Ojs.String))
val join: t -> string -> string [@@js.call]
end
```
By moving the type parameters to the functor arguments, you can enforce the
value conversion between JS types and OCaml types.
You can also use [first-class modules](VALUES.md#first-class-modules) for value bindings,
which will be used to convert the polymorphic values and thus making the binding safe:
```ocaml
module[@js.scope "console"] Console: sig
val log: (module[@js] Ojs.T with type t = 'a) -> 'a -> unit [@@js.global]
end
```
You can also create safe bindings manually with the low level functions
provided by `Ojs` module. See the [section on manually created bindings](LOW_LEVEL_BINDING.md)
for more information.
================================================
FILE: VALUES.md
================================================
Value bindings in gen_js_api
============================
Supported forms
---------------
- Method call:
```ocaml
val my_method: t -> T1 -> ... -> Tn -> T
[@@js.call]
```
Calling the function on a first argument `o` of type `t` corresponds
to calling the method `myMethod` on the underlying JS object, with
other arguments passed to it.
By default, the name of the method on the JS side is derived from
the name of the OCaml value (`myMethod` above). It is possible to
specify a custom name explicitly, for instance for cases where the
JS name is not a valid OCaml (lowercase-)identifier, or to support
overloading (exposing multiple OCaml functions that correspond to
different types given to the same JS method):
```ocaml
val my_method: t -> T1 -> ... -> Tn -> T
[@@js.call "JavaScriptMethodName"]
```
- Function Application
```ocaml
val apply: t -> T1 -> ... -> Tn
[@@js.apply]
```
Calling the function on a first argument `f` of type `t` corresponds
to calling the underlying JS function object directly, with
other arguments passed to it.
This is particularly useful when binding to a "callable" JS object (an object that is also a function), or [a function type in a TypeScript interface](https://www.typescriptlang.org/docs/handbook/interfaces.html#function-types).
The name of the function need not necessarily be `apply` as long as the `[@@js.apply]` attribute is present.
When the function you want to bind is a "newable" one (a function that must be called with a prefix `new`, e.g. constructors), use `[@@js.apply_newable]` instead. This is especially useful to bind to constructor interfaces in TypeScript.
```ocaml
module FooConstructor: sig
type t
val new_: t -> Foo.t [@@js.apply_newable]
end
val fooConstructor: FooConstructor.t [@@js.global "Foo"]
```
When the "callable" object you want to bind to is a global object, the `[@@js.invoke]`
attribute along with the `[@js.scope]` attribute (see below) may be used to call it.
For instance, you can write
```ocaml
module[@js.scope "JavaScriptClassName"] C : sig
val invoke: T1 -> ... -> Tn -> t [@@js.invoke]
end
(* usage *)
let x = C.invoke arg1 ... argn
```
instead of
```ocaml
module C : sig
type t
val apply: t -> T1 -> ... -> Tn -> t [@@js.apply]
end
val c: C.t [@@js.global "JavaScriptClassName"]
(* usage *)
let x = C.apply c arg1 ... argn
```
- Object constructor:
```ocaml
val new_my_class: T1 -> ... -> Tn -> t
[@@js.new]
```
Corresponds to calling a JS constructor with arguments
passed to it.
By default, the name of the class on the JS side is derived from the
name of the OCaml value (`MyClass` above): in this case, the value
name must start with the `new_` prefix which is dropped and the
remaining name is capitalize to obtain the class name. It is
also possible to specify a custom name explicitly.
```ocaml
val f: T1 -> ... -> Tn -> t
[@@js.new "JavaScriptClassName"]
```
As for global values, it is possible to indicate the access path by
using `[@js.scope]` attributes on englobing modules (see below).
When the global object is itself an object constructor, the `[@@js.create]`
attribute may be used to instantiate it.
For instance,
```ocaml
module[@js.scope "JavaScriptClassName"] C : sig
val create: T1 -> ... -> Tn -> t [@@js.create]
end
```
is the same as
```ocaml
module C : sig
val create: T1 -> ... -> Tn -> t [@@js.new "JavaScriptClassName"]
end
```
- Global value or function:
```ocaml
val x: t
[@@js.global]
```
This creates an OCaml value that corresponds to a globally accessible
JavaScript value. This is used to access both global objects (e.g.
the `window` object) and global functions (e.g. `alert`). It is also
possible to specify a custom name for the JavaScript variable:
```ocaml
val x: t
[@@js.global "JavaScriptValueName"]
```
Example:
```ocaml
val alert: string -> unit
[@@js.global]
```
By default, a global value or function is taken from the global
object. However, it is possible to specify an access path by using
`[@js.scope]` attribute on englobing modules (see the Scope section).
- Property getter
```ocaml
val prop: t -> T
[@@js.get]
```
Calling the function on a first argument `o` of type `t` corresponds
to getting the `prop` property of the underlying JS object. A custom
name for the JS property can be specified:
```ocaml
val get_property: t -> T
[@@js.get "MyProp"]
```
- Property setter
```ocaml
val set_prop: t -> T -> unit
[@@js.set]
```
Calling the function on a first argument `o` of type `t` corresponds
to setting the `prop` property of the underlying JS object. Note that
the value name must start with the `set_` prefix, which is dropped to
obtain the property name.
A custom name for the JS property can also be specified (in which
case the name of the value can be arbitrary):
```ocaml
val modify_prop: t -> T -> unit
[@@js.set "prop"]
```
- Index getter
```ocaml
val get: t -> index -> T option
[@@js.index_get]
```
Corresponds to getting from an index accessor or [an index signature in a TypeScript interface](https://www.typescriptlang.org/docs/handbook/interfaces.html#indexable-types).
The return type may be `T` or `T option`, depending on whether the property is optional or not.
The name of the function need not necessarily be `get` as long as the `[@@js.index_get]` attribute is present.
`index` must be `int`, `string`, or abstract types holding a JavaScript `number` or `string` value.
- Index setter
```ocaml
val set: t -> index -> T -> unit
[@@js.index_set]
```
Corresponds to setting to an index accessor or [an index signature in a TypeScript interface](https://www.typescriptlang.org/docs/handbook/interfaces.html#indexable-types).
The name of the function need not necessarily be `set` as long as the `[@@js.index_set]` attribute is present.
`index` must be `int`, `string`, or abstract types holding a JavaScript `number` or `string` value.
- Global getter
```ocaml
val get_x: unit -> T
[@@js.get "x"]
val get_sub_x: unit -> T
[@@js.get "MyObject.x"]
```
This creates a function which returns the current value of a
global variable or of a (possibly nested) inner field of a global variable.
As for global values, it is possible to indicate the access path by
using `[@js.scope]` attributes on englobing modules.
- Global setter
```ocaml
val set_x: T -> unit
[@@js.set "x"]
val set_sub_x: T -> unit
[@@js.set "MyObject.x"]
```
This creates a function which sets the value of a
global variable or of a (possibly nested) inner field of a global variable.
As for global values, it is possible to indicate the access path by
using `[@js.scope]` attributes on englobing modules.
- Cast
```ocaml
val cast: t1 -> t2
[@@js.cast]
```
Calling this function performs an unchecked cast from type `t1` to
type `t2`, going through the JavaScript representation (i.e.
applying mapper from `t1` to the underlying JS object, and back
using the mapper for `t2`).
- Literal object builder:
```ocaml
val make: l1:T1 -> ... -> ln:tn -> t
[@@js.builder]
```
Corresponds to creating a JS plain object with fields initialized
with the provided values. The name of the function (`make` in the
example) does not correspond to any concept in JS. By default, the
JS field names are derived from OCaml labels, but it is also
possible to override that with a `[@js]` attribute on the argument's
type. All fields must be labeled or optional, or come with such an
attribute.
Optional arguments (but not non-optional argument with optional
type) are treated in a special way: no field is created in the JS
object if the parameter is not provided on the call site (without
this special behavior, the treatment would be to set the field to
`null`, which is the encoding of `None`).
Example:
```ocaml
type t = private Ojs.t
val mk: ?children:t list -> age:int -> (string[@js "name"]) -> t
[@@js.builder]
```
- Custom binding:
```ocaml
val f: ...
[@@js.custom
let f = ...
]
```
The val declaration itself doesn't produce anything in the
implementation. Instead, custom OCaml code that goes into the
implementation must be provided explicitly.
See [Verbatim section](IMPLGEN.md) for more details and examples.
Calling a function/constructor by different means
-------------------------------------------------
| | as a function | as a constructor |
|-------------------------------------|------------------------|------------------------|
| call the first argument | `[@@js.apply]` | `[@@js.apply_newable]` |
| call the global object | `[@@js.invoke]` | `[@@js.create]` |
| call a member of the first argument | `[@@js.call "methodName"]` | N/A |
| call a member of the global object | `[@@js.global "funcName"]` | `[@@js.new "ClassName"]` |
Scope
-----
The signature attribute `[@@@js.scope "property"]` changes the reference to the current global
object by following the property provided as payload. Nested scopes work as if the
access path were composed by concatenation of all the names indicated by [@js.scope]
attribute, separated by a '.'.
A simple use case is to bind to JavaScript values packed in singleton objects or classes.
For instance,
```ocaml
module[@js.scope "console"] Console: sig
val log: string -> unit [@@js.global]
end
```
is equivalent to
```ocaml
module Console: sig
val log: string -> unit [@@js.global "console.log"]
end
```
When attached directly to a module, the payload of `[@@js.scope]`
may be omitted, it will be implicitly filled with the module name
(preserving the capitalization !).
Before version 1.0.7, the presence of `[@@js.scope]` used to change
the behavior of automatic bindings. It is no longer the case.
An experimental feature also allows to pass an expression of type `Ojs.t` as
a payload to replace the global object. The intended use case is to allow
dynamic loading of modules.
There is also a tuple notation `[@js.scope (s1, ..., sn)]` that helps
writing nested scopes. It is equivalent to `[@js.scope sn]...[@js.scope s1]`.
For instance, the following annotated modules will generate the same code:
```ocaml
module NestedScope0 : sig
val f: string -> unit [@@js.global "outer.inner.f"]
end
module [@js.scope ("outer", "inner")] NestedScope1 : sig
val f: string -> unit [@@js.global]
end
module NestedScope2 : sig
val f: string -> unit [@@js.global]
end [@js.scope "inner"] [@js.scope "outer"]
```
First-class modules
-------------------
As introduced in [Type variables](TYPES.md#type-variables), you can use
first-class modules to enforce JS/OCaml value conversion on polymorphic functions.
```ocaml
module[@js.scope "console"] Console: sig
val log: (module[@js] Ojs.T with type t = 'a) -> 'a -> unit [@@js.global]
end
```
There are several restrictions when using first-class modules:
* First-class modules must be annotated with `@js`.
- This attribute indicates that it should only be used to convert values
and should not be passed directly to the JS function.
* First-class modules must come before any other normal types.
- The following is invalid because the first-class module comes after a normal type `'a`:
```ocaml
val log: 'a -> (module[@js] Ojs.T with type t = 'a) -> unit [@@js.global]
```
* A first-class module to convert a type variable `'x` must be in the form of
`(module[@js] Ojs.T with type t = 'x)`.
- The following is invalid because it has a different form (though the meaning is equivalent):
```ocaml
module type MyOjsT = Ojs.T
val log: (module[@js] MyOjsT with type t = 'a]) -> 'a -> unit [@@js.global]
```
* First-class modules can't be used outside of value bindings.
- The following is invalid because it is used in a type alias:
```ocaml
type 'a logger = (module[@js] Ojs.T with type t = 'a) -> 'a -> unit
val log: 'a logger [@@js.global]
```
To use bindings with first-class modules, you just have to pass the enclosing modules of the types:
```ocaml
module[@js.scope "Person"] Person : sig
type t
(* these functions must be present *)
val t_to_js: t -> Ojs.t
val t_of_js: Ojs.t -> t
val create: string -> t [@@js.create]
end
let p = Person.create "Foo";;
Console.log (module Person) p;; (* Person { name: 'Foo' } *)
```
For built-in types, there are pre-defined modules available in the `Ojs` module:
```ocaml
Console.log (module Ojs.String) "hello, world!";;
Console.log (module Ojs.Int) 42;;
Console.log (module Ojs.List(Ojs.String)) ["hello"; "world!"];;
```
Automatic binding (Deprecated since 1.0.7)
------------------------------------------
Some conventions, based on the declared value names and their types,
allow to infer implicitly the `[@@js.xxx]` attributes on value
declarations in most cases.
*This feature has been deprecated starting from version 1.0.7*. All values
declaration should be annotated with an explicit attribute. Otherwise
a preprocessor warning will be emitted.
Note that in all modes the declaration of conversion functions generated
from types are ignored in order to expose the generated functions.
This means all value of declarations of the form:
```ocaml
val τ_to_js: ... -> Ojs.t
```
or the form
```ocaml
val τ_of_js: ... -> τ
```
The rules are applied in order:
- If the value is a function whose result is a named type `... -> τ`
and the name is `create`, then the declaration is assumed to
be a `[@@js.create]` object creation.
- If the value is a function whose result is a named type `... -> τ`
and its name starts with `new_`, then the declaration is assumed to
be a `[@@js.new]` object creation (on the class whose name is
obtained by dropping the `new_`prefix).
- If the value is a function with a single argument `τ1 -> unit` and
its name starts with `set_`, then the declaration is assumed to be a
`[@@js.set]` global setter (whose name is obtained by dropping the
`set_` prefix).
- If the value is a function returning `unit` with three arguments
whose first argument is a named type `τ -> τ2 -> τ3 -> unit` and the
name is `set`, then the declaration is assumed to be a
`[@@js.set_index]` index setter.
- If the value is a function with two arguments `τ1 -> τ2 -> unit` and
its name starts with `set_`, then the declaration is assumed to be a
`[@@js.set]` property setter (on the property whose name is obtained
by dropping the `set_` prefix).
- If the value is a function with a single argument (named type) `τ ->
unit`, then the declaration is assumed to be a `[@@js.call]` method
call.
- If the value is a function with two arguments whose first argument is
a named type `τ -> τ2 -> τ3` (and `τ3` is not `unit`) and the name is
`get`, then the declaration is assumed to be a `[@@js.index_get]`
index getter.
- If the value is a function with a single argument (named type) `τ ->
τ2` (and `τ2` is not `unit`), then the declaration is assumed to be
a `[@@js.get]` property getter.
- If the value is a function with a single argument `unit -> τ2`, then
the declaration is assumed to be a `[@@js.get]` global getter.
- If the value is a function whose first argument is a named type `τ
-> ...` and the name is `apply`, then the definition is assumed to
be a `[@@js.apply]` function object application.
- If the value is a function whose first argument is a named type `τ
-> ...` (and the name is not `apply`), then the definition is assumed
to be a `[@@js.call]` method call.
- Otherwise, the declaration is assumed to be a `[@@js.global]` value.
This applies in particular for any non-functional type.
================================================
FILE: dune
================================================
(env
(dev
(flags (:standard))))
(deprecated_library_name
(old_public_name gen_js_api)
(new_public_name ojs))
================================================
FILE: dune-project
================================================
(lang dune 3.17)
(name gen_js_api)
(version 1.1.7)
(maintainers "Alain Frisch <alain.frisch@lexifi.com>")
(authors
"Alain Frisch <alain.frisch@lexifi.com>"
"Sebastien Briais <sebastien.briais@lexifi.com>")
(source (github LexiFi/gen_js_api))
(generate_opam_files true)
(license MIT)
(package
(name ojs)
(synopsis "Runtime Library for gen_js_api generated libraries")
(description "To be used in conjunction with gen_js_api")
(depends
(ocaml (>= 4.13))
(js_of_ocaml-compiler (>= 6.3.0)))
)
(package
(name gen_js_api)
(synopsis "Easy OCaml bindings for JavaScript libraries")
(description "
gen_js_api aims at simplifying the creation of OCaml bindings for
JavaScript libraries. Authors of bindings write OCaml signatures for
JavaScript libraries and the tool generates the actual binding code
with a combination of implicit conventions and explicit annotations.
gen_js_api is to be used with the js_of_ocaml compiler.
")
(conflicts (js_of_ocaml-compiler (< 6.3.0)))
(depends
(ocaml (>= 4.13))
(ppxlib (>= 0.37))
(js_of_ocaml-compiler :with-test)
(ojs (= :version)))
)
================================================
FILE: examples/calc/calc.html
================================================
<html>
<head>
<title>Calculator</title>
</head>
<body>
<script src="calc.js"></script>
</body>
</html>
================================================
FILE: examples/calc/calc.ml
================================================
module Element = [%js:
type t
val t_of_js: Ojs.t -> t
val append_child: t -> t -> unit [@@js.call]
val set_attribute: t -> string -> string -> unit [@@js.call]
val set_onclick: t -> (unit -> unit) -> unit [@@js.set]
]
module Window = [%js:
type t
val instance: t [@@js.global "window"]
val set_onload: t -> (unit -> unit) -> unit [@@js.set]
]
module Document = [%js:
type t
val instance: t [@@js.global "document"]
val create_element: t -> string -> Element.t [@@js.call]
val create_text_node: t -> string -> Element.t [@@js.call]
val body: t -> Element.t [@@js.get]
]
let element tag children =
let elt = Document.create_element Document.instance tag in
List.iter (Element.append_child elt) children;
elt
let textnode s = Document.create_text_node Document.instance s
let td ?colspan child =
let elt = element "td" [child] in
begin match colspan with
| None -> ()
| Some n -> Element.set_attribute elt "colspan" (string_of_int n)
end;
elt
let tr = element "tr"
let table = element "table"
let center x = element "center" [x]
let button x f =
let elt = element "button" [textnode x] in
Element.set_attribute elt "type" "button";
Element.set_onclick elt f;
elt
module Engine = struct
type op = Add | Sub | Mul | Div
type state =
{
x: float;
y: float;
operator: op option;
input: bool;
equal: bool;
comma: int;
}
let initial = { x = 0.; y = 0.; operator = None; input = false; equal = false; comma = 0 }
let make_op op x y =
match op with
| Add -> x +. y
| Sub -> x -. y
| Mul -> x *. y
| Div -> x /. y
let of_digit d = float_of_int d
let add_digit x comma d =
if comma = 0 then 10. *. x +. float_of_int d, comma
else x +. float_of_int d /. (10. ** (float_of_int comma)), comma + 1
let input_digit ({x; y; operator = _; input; equal; comma} as state) d =
let y = if equal then y else x in
let x, comma =
if input then add_digit x comma d
else of_digit d, 0
in
{state with x; y; comma; input = true}
let apply_comma ({input; comma; _} as state) =
if comma = 0 then
if input then {state with comma = 1}
else {(input_digit state 0) with comma = 1}
else state
let apply_equal ({x; y; operator; input; equal; comma = _} as state) =
match operator with
| None -> {state with y = x; input = false; equal = true}
| Some o ->
if input && not equal then {state with x = make_op o y x; y = x; input = false; equal = true}
else {state with x = make_op o x y; equal = true}
let apply_op ({input; equal; _} as state) op =
if input && not equal then {(apply_equal state) with operator = Some op; equal = false}
else {state with operator = Some op; equal= false; input = false}
let print_op ppf = function
| None -> Printf.fprintf ppf " "
| Some Add -> Printf.fprintf ppf "+"
| Some Sub -> Printf.fprintf ppf "-"
| Some Mul -> Printf.fprintf ppf "*"
| Some Div -> Printf.fprintf ppf "/"
let print ppf {x; y; operator; input; equal; comma} =
Printf.fprintf ppf "x = %g, y = %g, op = %a, input = %b, equal = %b, comma = %d" x y print_op operator input equal comma
end
let widget () =
let open Engine in
let state = ref initial in
let res, set_value =
let elt = element "input" [] in
Element.set_attribute elt "type" "text";
Element.set_attribute elt "readonly" "";
let set_value v = Element.set_attribute elt "value" (string_of_float v) in
elt, set_value
in
let update st =
Printf.printf "%a\n" print st;
state := st;
set_value !state.x
in
let reset() = update initial in
reset();
let binop op () = update (apply_op !state op) in
let equal () = update (apply_equal !state) in
let comma () = update (apply_comma !state) in
let figure digit =
let f () = update (input_digit !state digit) in
button (string_of_int digit) f
in
let c l = td l in
let nothing () = element "div" [] in
table [tr [td ~colspan:4 res];
tr (List.map c [nothing(); button "C" reset; nothing(); button "/" (binop Div)]);
tr (List.map c [figure 7; figure 8; figure 9; button "*" (binop Mul)]);
tr (List.map c [figure 4; figure 5; figure 6; button "-" (binop Sub)]);
tr (List.map c [figure 1; figure 2; figure 3; button "+" (binop Add)]);
tr (List.map c [nothing(); figure 0; button "." comma; button "=" equal])]
let go () =
Element.append_child (Document.body Document.instance) (center (widget()))
let () =
Window.set_onload Window.instance go
================================================
FILE: examples/calc/dune
================================================
(executables
(names calc)
(libraries ojs)
(preprocess
(pps gen_js_api.ppx))
(modes js))
(rule
(targets calc.js)
(deps calc.bc.js)
(action
(run cp %{deps} %{targets})))
(alias
(name DEFAULT)
(deps calc.js calc.html))
================================================
FILE: examples/misc/dune
================================================
(executables
(names test_jquery)
(libraries ojs)
(preprocess
(pps gen_js_api.ppx))
(modes js))
(rule
(targets jquery.ml)
(deps jquery.mli)
(action
(run %{bin:gen_js_api} %{deps})))
(rule
(targets js_date.ml)
(deps js_date.mli)
(action
(run %{bin:gen_js_api} %{deps})))
(rule
(targets js_str.ml)
(deps js_str.mli)
(action
(run %{bin:gen_js_api} %{deps})))
(rule
(targets test_jquery.js)
(deps test_jquery.bc.js)
(action
(run cp %{deps} %{targets})))
(alias
(name DEFAULT)
(deps test_jquery.js test_jquery.html))
================================================
FILE: examples/misc/jquery.mli
================================================
(* The gen_js_api is released under the terms of an MIT-like license. *)
(* See the attached LICENSE file. *)
(* Copyright 2015 by LexiFi. *)
(** Partial binding to jQuery, serving as an illustration
of gen_js_api. The binding is far from complete! *)
[@@@js.implem [@@@ocaml.warning "-22"]]
(** {2 Sets of elements} *)
type t = private Ojs.t
val selector: string -> t
[@@js.global "jQuery"]
(** Either select a set of elements from the current document, or
create a new element (if given a string such as "<div>". *)
val wrap: Ojs.t -> t [@@js.global "jQuery"]
val explode: t -> t list
[@@js.custom let explode x = Ojs.list_of_js wrap x]
val find: t -> string -> t list
[@@js.custom
val find: t -> string -> t [@@js.call "find"]
let find x sel = explode (find x sel)
]
val text: t -> string
[@@js.call]
val set_text: t -> string -> unit
[@@js.call "text"]
val update_text: t -> (int -> string -> string) -> unit
[@@js.call "text"]
val append_html: t -> string -> unit
[@@js.call "append"]
val append: t -> (t list [@js.variadic]) -> unit
[@@js.call "append"]
val prepend: t -> (t list [@js.variadic]) -> unit
[@@js.call]
val after: t -> t -> unit
[@@js.call]
val before: t -> t -> unit
[@@js.call]
val get_val: t -> string
[@@js.call "val"]
val hide: t -> unit
[@@js.call]
val show: t -> unit
[@@js.call]
val detach: t -> unit
[@@js.call]
val remove: t -> unit
[@@js.call]
val empty: t -> unit
[@@js.call]
val focus: t -> unit
val height: t -> int [@@js.call]
val set_height: t -> ([`String of string | `Int of int] [@js.union]) -> unit [@@js.call "height"]
val width: t -> int [@@js.call]
val set_width: t -> ([`String of string | `Int of int] [@js.union]) -> unit [@@js.call "width"]
val string_value: t -> string [@@js.call "val"]
val set_string_value: t -> string -> unit [@@js.call "val"]
val add_class: t -> string -> unit [@@js.call]
val remove_class: t -> string -> unit [@@js.call]
val css: t -> string -> Ojs.t [@@js.call]
val set_css_value: t -> string -> ([`String of string | `Float of float] [@js.union]) -> unit [@@js.call "css"]
val set_css: t -> Ojs.t -> unit [@@js.call "css"]
val clone: t -> t [@@js.call]
val html: t -> string
[@@js.call "html"]
val set_html: t -> string -> unit
[@@js.call "html"]
(** {2 Properties} *)
val prop: t -> string -> Ojs.t
[@@js.call]
val set_prop:
t -> string ->
([`String of string | `Int of int | `Bool of bool | `Any of Ojs.t] [@js.union]) ->
unit
[@@js.call "prop"]
(** {2 Data} *)
val data: t -> string -> Ojs.t
[@@js.call]
val set_data: t -> string -> Ojs.t -> unit
[@@js.call "data"]
(** {2 Attributes} *)
val attr: t -> string -> string option
[@@js.call]
val set_attr: t -> string -> string -> unit
[@@js.call "attr"]
val remove_attr: t -> string -> unit
[@@js.call]
(** {2 Animations} *)
val fade_in: t -> ?duration:int -> ?finished:(unit -> unit) -> unit -> unit
[@@js.call]
val fade_out: t -> ?duration:int -> ?finished:(unit -> unit) -> unit -> unit
[@@js.call]
(** {2 Events} *)
module Event : sig
type t
val page_x: t -> float
val page_y: t -> float
val type_: t -> string
val target: t -> Ojs.t
val which: t -> int
val stop_propagation: t -> unit [@@js.call]
val prevent_default: t -> unit [@@js.call]
end
val on: t -> string -> (Event.t -> unit) -> unit
val off: t -> string -> unit
val trigger: t -> string -> unit
[@@js.call]
val ready: (unit -> unit) -> unit
[@@js.global "jQuery"]
module Dialog: sig
type button
val button:
text:string ->
click:(unit -> unit) ->
unit -> button
[@@js.builder]
type settings
val settings:
?modal:bool ->
?title:string ->
?buttons:button list ->
unit -> settings
[@@js.builder]
end
module UI : sig
module Datepicker : sig
type settings
val settings:
?date_format:string ->
unit -> settings
[@@js.builder]
end
val datepicker: t -> Datepicker.settings -> unit
end
val dialog: t -> ([`Dialog of Dialog.settings | `String of string] [@js.union]) -> unit
(** {2 AJAX} *)
module Ajax: sig
type settings
(** The type describing all settings of an AJAX call. *)
type t
(** Corresponds to jQuery's jqXHR object. *)
val settings:
?async:bool ->
?cache:bool ->
?complete:(t -> string -> unit) ->
?error:(t -> string -> string -> unit) ->
?success:(Ojs.t -> string -> t -> unit) ->
?data:Ojs.t -> ?data_type:string ->
?meth:([`GET | `POST | `PUT] [@js "method"] [@js.enum]) ->
?content_type:string ->
?url:string ->
unit -> settings
[@@js.builder]
val run: settings -> unit
[@@js.global "jQuery.ajax"]
val response_text: t -> string
val status: t -> int
end
================================================
FILE: examples/misc/js_date.mli
================================================
(* The gen_js_api is released under the terms of an MIT-like license. *)
(* See the attached LICENSE file. *)
(* Copyright 2015 by LexiFi. *)
(** JS dates *)
(** {2 Type definitions} *)
type t = private Ojs.t
val t_of_js: Ojs.t -> t
val t_to_js: t -> Ojs.t
val now: unit -> t [@@js.new "Date"]
val from_milliseconds: float -> t [@@js.new "Date"]
val from_string: string -> t [@@js.new "Date"]
val create: year:int -> month:int -> ?day:(int [@js.default 1]) -> ?hours:(int [@js.default 0]) -> ?minutes:(int [@js.default 0]) -> ?seconds:(int [@js.default 0]) -> ?ms:(int [@js.default 0]) -> unit -> t [@@js.new "Date"]
val get_UTC_date: t -> int [@@js.call]
val get_UTC_day: t -> int [@@js.call]
val get_UTC_full_year: t -> int [@@js.call]
val get_UTC_hours: t -> int [@@js.call]
val get_UTC_milliseconds: t -> int [@@js.call]
val get_UTC_minutes: t -> int [@@js.call]
val get_UTC_month: t -> int [@@js.call]
val get_UTC_seconds: t -> int [@@js.call]
val set_UTC_date: t -> int -> unit [@@js.call]
val set_UTC_full_year: t -> int -> unit [@@js.call]
val set_UTC_hours: t -> int -> unit [@@js.call]
val set_UTC_milliseconds: t -> int -> unit [@@js.call]
val set_UTC_minutes: t -> int -> unit [@@js.call]
val set_UTC_month: t -> int -> unit [@@js.call]
val set_UTC_seconds: t -> int -> unit [@@js.call]
val get_date: t -> int [@@js.call]
val get_day: t -> int [@@js.call]
val get_full_year: t -> int [@@js.call]
val get_hours: t -> int [@@js.call]
val get_milliseconds: t -> int [@@js.call]
val get_minutes: t -> int [@@js.call]
val get_month: t -> int [@@js.call]
val get_seconds: t -> int [@@js.call]
val set_date: t -> int -> unit [@@js.call]
val set_full_year: t -> int -> unit [@@js.call]
val set_hours: t -> int -> unit [@@js.call]
val set_milliseconds: t -> int -> unit [@@js.call]
val set_minutes: t -> int -> unit [@@js.call]
val set_month: t -> int -> unit [@@js.call]
val set_seconds: t -> int -> unit [@@js.call]
val get_time: t -> float [@@js.call]
val set_time: t -> float -> unit [@@js.call]
val get_timezone_offset: t -> int [@@js.call]
val to_locale_date_string: t -> string [@@js.call]
val to_locale_string: t -> string [@@js.call]
val to_locale_time_string: t -> string [@@js.call]
val to_date_string: t -> string [@@js.call]
val to_time_string: t -> string [@@js.call]
val to_UTC_string: t -> string [@@js.call]
val to_string: t -> string [@@js.call]
================================================
FILE: examples/misc/js_str.mli
================================================
(* The gen_js_api is released under the terms of an MIT-like license. *)
(* See the attached LICENSE file. *)
(* Copyright 2015 by LexiFi. *)
(** JS string and regexp objects *)
(** {2 Type definitions} *)
type t = private Ojs.t
val t_of_js: Ojs.t -> t
val t_to_js: t -> Ojs.t
type regexp = private Ojs.t
val regexp_of_js: Ojs.t -> regexp
val regexp_to_js: regexp -> Ojs.t
(** {2 Conversion between JS strings and OCaml string} *)
val to_string: t -> string [@@js.cast]
val of_string: string -> t [@@js.cast]
(** {2 JS strings} *)
val from_char_code: (int list [@js.variadic]) -> t
[@@js.global "String.fromCharCode"]
val char_at: t -> int -> t
val char_code_at: t -> int -> int
val concat: t -> (t list [@js.variadic]) -> t
val index_of: t -> t -> ?start:int -> unit -> int
val last_index_of: t -> t -> ?start:int -> unit -> int
val length: t -> int
val locale_compare: t -> t -> int
val match_: t -> regexp -> t array option
val replace: t -> regexp -> t -> t
val search: t -> regexp -> int
val slice: t -> start:int -> ?end_:int -> unit -> t
val split: t -> ?separator:t -> ?limit:int -> unit -> t array
val substr: t -> start:int -> ?length:int -> unit -> t
val substring: t -> start:int -> ?end_:int -> unit -> t
val to_locale_lower_case: t -> t [@@js.call]
val to_locale_upper_case: t -> t [@@js.call]
val to_lower_case: t -> t [@@js.call]
val to_upper_case: t -> t [@@js.call]
val trim: t -> t [@@js.call]
(** {2 Regexps} *)
val regexp: t -> ?global:unit -> ?ignore_case:unit -> ?multiline:unit -> unit -> regexp
[@@js.custom
val regexp_internal: t -> ?flags:t -> unit -> regexp [@@js.new "RegExp"]
let regexp txt ?global ?ignore_case ?multiline () =
let l = [] in
let l = match global with Some () -> of_string "g" :: l | None -> l in
let l = match ignore_case with Some () -> of_string "i" :: l | None -> l in
let l = match multiline with Some () -> of_string "m" :: l | None -> l in
regexp_internal txt ~flags:(concat (of_string "") l) ()
]
val global: regexp -> bool
val ignore_case: regexp -> bool
val multiline: regexp -> bool
val source: regexp -> string
val last_index: regexp -> int
val exec: regexp -> t -> t array option
val test: regexp -> t -> bool
================================================
FILE: examples/misc/test_jquery.html
================================================
<html>
<head>
</head>
<body>
<span class="tofill">One</span>
<span class="tofill">Two</span>
<div id="main">Blabla</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<script type="text/javascript" src="test_jquery.js">
</script>
</body>
</html>
================================================
FILE: examples/misc/test_jquery.ml
================================================
(* The gen_js_api is released under the terms of an MIT-like license. *)
(* See the attached LICENSE file. *)
(* Copyright 2015 by LexiFi. *)
(** A toy application built with jQuery *)
open Jquery
include [%js:
val alert: string -> unit
[@@js.global]
]
let ( !! ) = Jquery.selector
let block s ?text ?(classes = []) ?(ons = []) ?(css = []) ?(props = []) children =
let element = Jquery.selector (Printf.sprintf "<%s>" s) in
begin match text with
| None -> ()
| Some text -> Jquery.set_text element text
end;
List.iter (fun c -> Jquery.add_class element c) classes;
List.iter (fun (key, value) -> Jquery.set_css_value element key value) css;
List.iter (fun (key, value) -> Jquery.set_prop element key value) props;
List.iter (fun (event, f) -> Jquery.on element event f) ons;
begin match children with
| [] -> ()
| _ :: _-> Jquery.append element children
end;
element
let ajax_test () =
let open Ajax in
let complete h = function
| "success" ->
let pre = block "pre" ~text:(response_text h) [] in
hide pre;
append !!"body" [pre];
fade_in pre ~duration:2000
~finished:(fun () ->
fade_out pre ~finished:(fun () -> detach pre) ()
)
()
| status -> alert (Printf.sprintf "status = %s" status)
in
run (settings ~meth:`GET ~url:"test_jquery.ml" ~data_type:"text" ~complete ())
let on_ready () =
let main = !!"#main" in
print_endline (text main);
set_text main "Hello world!";
append_html main "<b>in bold</b>";
let elts = !!".tofill" in
update_text elts (Printf.sprintf "[%i:%s]");
append main [elts; !! "<b>XXX</b>"];
let on_click evt =
let open Event in
append_html main
(Printf.sprintf "<br/>x=%f,y=%f,type=%s"
(page_x evt)
(page_y evt)
(type_ evt)
)
in
on main "click" on_click;
let div = block "div" [] in
let input = block "input" [] in
on input "input" (fun _ -> set_text div (get_val input));
append main [input; div];
let btn =
block "button" ~text:"SHOW SOURCE CODE" []
~ons:["click", (fun _ -> ajax_test ())]
in
append main [btn]
let () =
ready on_ready
================================================
FILE: examples/test/dune
================================================
(executables
(names main)
(libraries ojs)
(preprocess
(pps gen_js_api.ppx))
(modes js))
(rule
(targets test_bindings.ml)
(deps test_bindings.mli)
(action
(run gen_js_api %{deps})))
(rule
(targets main.js)
(deps main.bc.js)
(action
(run cp %{deps} %{targets})))
(alias
(name DEFAULT)
(deps main.js main.html))
================================================
FILE: examples/test/main.html
================================================
<html>
<head>
</head>
<body>
<div class="myClass">Blabla</div>
<canvas id="canvas" width="200" height="200">
Bla
</canvas>
<script type="text/javascript">
var myArray = [1, 2, 3];
function wrapper(f) {
return function (x, y) {
console.log("Before");
var r = f(x, y);
console.log("Result 1 = " + r);
var r = f(r, y);
console.log("Result 2 = " + r);
return r;
}
}
function caller(f) {
console.log("Before call");
var r = f();
console.log("After call");
return r;
}
function Person(name, foo) {
console.log(arguments);
this.name = name;
this.foo = foo;
this.get = function () {
return [this.name, this.foo];
};
this.set = function (a) {
this.name = a[0];
this.foo = a[1];
};
console.log("New Person object created");
}
function testVariadic(f) {
console.log(f());
console.log(f(0));
console.log(f(0, 1));
console.log(f(0, 1, 2));
console.log(f(0, 1, 2, 3));
console.log(f(0, 1, 2, 3, 4));
}
function testVariadic2(f) {
console.log(f("Hello"));
console.log(f("Hello", 0));
console.log(f("Hello", 0, 1));
console.log(f("Hello", 0, 1, 2));
console.log(f("Hello", 0, 1, 2, 3));
console.log(f("Hello", 0, 1, 2, 3, 4));
}
function testOptArgs(f) {
console.log(f());
console.log(f(10));
console.log(f(10, 10));
}
function test_sum() {
print_sum({kind:"A"});
print_sum({kind:"B", arg:806});
print_sum({kind:"C", arg:[806, "bar"]});
print_sum({kind:"D", age:24, name:"Toto"});
}
var myDict = { "gen_js_api": "OK", "js_of_ocaml": "OK" };
function test_flatten(kind) {
switch(kind) {
case "A": {
console.log("A");
break;
}
case "B": {
console.log("B "+arguments[1]);
break;
}
case "C": {
console.log("C "+arguments[1]);
break;
}
case "D": {
console.log("D ("+arguments[1]+", "+arguments[2]+")");
break;
}
}
}
function test_typvars(anything) {
return [anything, anything]
}
function makeRef(anything) {
return {current: anything}
}
function eitherLeft(anything) {
return {left: anything};
}
function eitherRight(anything) {
return {right: anything};
}
function eitherDestruct(x, left, right) {
if (x.hasOwnProperty("left")) {
return left(x.left);
} else {
return right(x.right);
}
}
</script>
<script type="text/javascript" src="main.js">
</script>
<script type="text/javascript">
console.log(myArray);
</script>
</body>
</html>
================================================
FILE: examples/test/main.ml
================================================
(* The gen_js_api is released under the terms of an MIT-like license. *)
(* See the attached LICENSE file. *)
(* Copyright 2015 by LexiFi. *)
(** Some ad hoc code to illustrate and test various aspects
of gen_js_api *)
[@@@ocaml.warning "-32-34"]
open Test_bindings
[@@@ocaml.warning "-22"]
include
[%js:
val wrapper: (int -> int -> int) -> (int -> int -> int [@js.dummy])
[@@js.global "wrapper"]
val caller: (unit -> int) -> int
[@@js.global "caller"]
val caller_unit: (unit -> unit) -> unit
[@@js.global "caller"]
val test_variadic: ((int list [@js.variadic]) -> int) -> unit
val test_variadic2: (string -> (int list [@js.variadic]) -> int) -> unit
]
module LocalBindings = [%js:
type myType = { x : a; y : b [@js "Y"]}
and a = int option
and b = { s : string; i : int }
]
let () =
let s = [%js.of: int list] [10; 20; 30] in
Printf.printf "%i\n%!" ([%js.to: int] (Ojs.array_get s 0));
Printf.printf "%i\n%!" ([%js.to: int] (Ojs.array_get s 1));
Printf.printf "%i\n%!" ([%js.to: int] (Ojs.array_get s 2))
let () =
let sum xs = List.fold_left ( + ) 0 xs in
test_variadic sum;
test_variadic2 (fun msg xs -> Printf.printf "%s\n%!" msg; sum xs)
include [%js:
val myArray: int array
[@@js]
val myArray2: Ojs.t
[@@js.global "myArray"]
val alert_bool: bool -> unit
[@@js.global "alert"]
val alert_float: float -> unit
[@@js.global "alert"]
val test_opt_args: (?foo:int -> ?bar:int -> unit-> string) -> unit
[@@js.global]
]
let doc = Window.document window
let elt name ?(attrs = []) ?onclick subs =
let e = Document.createElement doc name in
List.iter (fun (k, v) -> Element.setAttribute e k v) attrs;
List.iter (Element.appendChild e) subs;
begin match onclick with
| Some f -> Element.set_onclick e f
| None -> ()
end;
e
let txt =
Document.createTextNode doc
let button ?attrs s onclick =
elt "button" ?attrs ~onclick [ txt s ]
let div = elt "div"
let () =
Array.iter (Printf.printf "[%i]\n") myArray;
Ojs.array_set myArray2 0 (Ojs.int_to_js 10);
Ojs.array_set myArray2 1 (Ojs.array_to_js Ojs.int_to_js [| 100; 200; 300 |]);
(* Ojs.array_set myArray2 1 ([%to_js: int array] [| 100; 200; 300 |]); *)
(*
Printf.printf "%0.2f\n" 3.1415;
*)
(*
Document.set_title doc "MyTitle";
Document.set_title doc (Document.title doc ^ " :-)");
*)
(* let main = Document.getElementById doc "main" in *)
(* print_endline (Element.innerHTML main); *)
(* alert (Element.innerHTML main); *)
(* Element.set_innerHTML main "<b>Bla</b>blabla"; *)
let draw () =
let canvas_elt = Document.getElementById doc "canvas" in
let canvas = Canvas.of_element canvas_elt in
let ctx = Canvas.getContext_2d canvas in
Canvas.RenderingContext2D.(begin
set_fillStyle ctx "rgba(0,0,255,0.1)";
fillRect ctx 30 30 50 50
end);
Element.set_onclick canvas_elt (fun () -> alert "XXX");
in
alert_bool true;
alert_float 3.1415;
let f =
wrapper
(fun x y ->
Printf.printf "IN CALLBACK, x = %i, y = %i\n%!" x y;
x + y
)
in
Printf.printf "Result -> %i\n%!" (f 42 1);
let uid = ref 0 in
let f () =
incr uid;
Printf.printf "uid = %i\n%!" !uid;
!uid
in
Printf.printf "Caller result -> %i, %i, %i\n%!" (caller f) (caller f) (caller f);
caller_unit (fun () -> ignore (f ()));
caller_unit (fun () -> ignore (f ()));
caller_unit (fun () -> ignore (f ()));
let alice = Person.create "Alice" Person.Foo.Foo in
let bob = Person.create "Bob" Person.Foo.Bar in
let charlie = Person.create "Charlie" (Person.Foo.OtherString "bla") in
let eve = Person.create "Eve" (Person.Foo.OtherInt 2713) in
Ojs.iter_properties (Person.cast alice) (Format.printf "%s\n%!");
let alice_obj = PersonObj.create "Alice" Person.Foo.Foo in
let bob_obj = PersonObj.of_person bob in
let dave_obj = new PersonObj.person "Dave" Person.Foo.Bar [1; 2; 3] in
let string_of_foo = function
| Person.Foo.Foo -> "foo"
| Person.Foo.Bar -> "bar"
| Person.Foo.OtherInt n -> Printf.sprintf "other = %d" n
| Person.Foo.OtherString s -> Printf.sprintf "other = %s" s
in
let string_of_name_foo name foo = Printf.sprintf "%s <%s>" name (string_of_foo foo) in
let string_of_person x = string_of_name_foo (Person.name x) (Person.foo x) in
let string_of_person_obj x = string_of_name_foo (x # name) (x # foo) in
let hack_person x =
let name, foo = Person.get x () in
Printf.printf "before: %s <%s>\n" name (string_of_foo foo);
Person.set x ("Dave", Person.Foo.OtherString "bar");
let name, foo = Person.get x () in
Printf.printf "after: %s <%s>\n" name (string_of_foo foo);
in
let body = Document.body doc in
setTimeout (fun () -> Element.setAttribute body "bgcolor" "red") 2000;
Element.appendChild body (Document.createTextNode doc "ABC");
Element.appendChild body
(div ~attrs:["style", "color: blue"] [ txt "!!!!"; elt "b" [txt "XXX"]]);
Element.appendChild body
(div (List.map (fun x -> txt (string_of_person x)) [alice; bob; charlie; eve]));
hack_person eve;
Element.appendChild body
(div (List.map (fun x -> txt (string_of_person x)) [alice; bob; charlie; eve]));
Element.appendChild body
(div (List.map (fun x -> txt (string_of_person_obj x)) [alice_obj; bob_obj; dave_obj]));
let s = (new Str.str "") # concat [Str.create "Hello"; Str.create ", "; Str.create "world"; Str.create "!"] in
Console.log_string console (s # to_string);
Console.log_string console (Date.to_string (Date.create ~year:2015 ~month:4 ~day:10 ()));
let l = Document.getElementsByClassName doc "myClass" in
Array.iter
(fun e ->
Printf.printf "- [%s]\n" (Element.innerHTML e); (* OK *)
print_string (Printf.sprintf "+ [%s]\n" (Element.innerHTML e)); (* BAD *)
Element.appendChild e (button "Click!" draw);
Element.appendChild e (button "XXX" (fun () -> ()));
)
l;
test_opt_args
(fun ?(foo = 0) ?(bar = 0) () -> string_of_int foo ^ "/" ^ string_of_int bar);
print_endline Person2.(to_json (mk ~children:[mk ~age:6 "Johnny"] ~age:42 "John Doe"))
(* Custom mapping between association lists and JS objects *)
module Dict : sig
type 'a t = (string * 'a) list
val t_to_js: ('a -> Ojs.t) -> 'a t -> Ojs.t
val t_of_js: (Ojs.t -> 'a) -> Ojs.t -> 'a t
end = struct
type 'a t = (string * 'a) list
let t_to_js ml2js l =
let o = Ojs.empty_obj () in
List.iter (fun (k, v) -> Ojs.set_prop o (Ojs.string_to_js k) (ml2js v)) l;
o
let t_of_js js2ml o =
let l = ref [] in
Ojs.iter_properties o
(fun k -> l := (k, js2ml (Ojs.get_prop o (Ojs.string_to_js k))) :: !l);
!l
end
include [%js:
val int_dict_to_json_string: int Dict.t -> string
[@@js.global "JSON.stringify"]
val myDict: string Dict.t
[@@js.global "myDict"]
val set_x: int -> unit
[@@js.set "x"]
val get_x: unit -> int
[@@js.get "x"]
]
let () =
print_endline (int_dict_to_json_string ["hello", 1; "world", 2]);
List.iter (fun (k, v) -> Printf.printf "%s -> %s\n%!" k v) myDict;
set_x 42;
print_endline (string_of_int (get_x ()))
module Sum = struct
include [%js:
type t =
| A
| B of int
| C of int * string
| D of {age:int; name:string}
[@@js.sum]
val t_of_js: Ojs.t -> t
val t_to_js: t -> Ojs.t
]
let print = function
| A -> print_endline "A"
| B n -> print_endline (Format.sprintf "B %d" n)
| C (n, s) -> print_endline (Format.sprintf "C (%d, %S)" n s)
| D {age; name} -> print_endline (Format.sprintf "D {age = %d; name = %S}" age name)
include [%js:
val set_print_sum: (t -> unit) -> unit
[@@js.set "print_sum"]
val test_sum: unit -> unit
[@@js.global "test_sum"]
]
let () =
set_print_sum print
let () = test_sum ()
let () =
Console.log console ([%js.of:t] A);
Console.log console ([%js.of:t] (B 42));
Console.log console ([%js.of:t] (C (42, "foo")));
Console.log console ([%js.of:t] (D {age=42; name="foo"}))
let () =
Console3.log 1;
Console3.log2 1 "two";
Console3.log3 1 "two" [];
Console3.log4 1 "two" [] [|4|]
let () =
Console4.log (module Ojs.Int) 1;
Console4.log2 (module Ojs.Int) (module Ojs.String) 1 "two";
Console4.log3 (module Ojs.Int) (module Ojs.String) (module Ojs.List(Ojs.Int)) 1 "two" [3]
end
include [%js:
val test_flatten: ([`A | `B of int | `C of string | `D of int * string] [@js.enum]) -> unit
[@@js.global "test_flatten"]
]
let () =
test_flatten `A;
test_flatten (`B 42);
test_flatten (`C "hello");
test_flatten (`D (42, "foo"))
include [%js:
val make_string : 'a -> string [@@js.global "String"]
]
let () =
Console3.log (make_string 1234);
Console3.log (make_string "string");
Console3.log (make_string ["list"]);
Console3.log (make_string [|"array"|])
include [%js:
val test_typvars: 'a -> 'a * 'a
[@@js.global "test_typvars"]
]
let () =
Console3.log (test_typvars `A);
Console3.log (test_typvars 1234);
Console3.log (test_typvars "string");
Console3.log (test_typvars ["list"])
let () =
let t = Ref.make "foo" in
Console3.log (Ref.current t);
Ref.setCurrent t "bar";
Console3.log (Ref.current t)
let () =
let foo = Either.left "foo" in
let foobar = Either.right ["foo"; "bar"] in
let f x = Either.destruct x ~left:(fun s -> s) ~right:(String.concat "-") in
Console3.log (Ojs.string_to_js (f foo));
Console3.log (Ojs.string_to_js (f foobar))
let () =
let open Variants.M3 in
let rec of_list = function
| [] -> Empty
| hd :: tl -> Cons (hd, of_list tl)
in
Console3.log ([%js.of: int t] (of_list [1;2;3]))
================================================
FILE: examples/test/test_bindings.mli
================================================
(* The gen_js_api is released under the terms of an MIT-like license. *)
(* See the attached LICENSE file. *)
(* Copyright 2015 by LexiFi. *)
(** Some ad hoc code to illustrate and test various aspects
of gen_js_api *)
[@@@js.implem [@@@ocaml.warning "-22"]]
module Element : sig
type t = private Ojs.t
val appendChild: t -> t -> unit
val set_innerHTML: t -> string -> unit
val innerHTML: t -> string
val set_onclick: t -> (unit -> unit) -> unit
val setAttribute: t -> string -> string -> unit
end
module Canvas : sig
module RenderingContext2D : sig
type t = private Ojs.t
val set_fillStyle: t -> string -> unit
val fillRect: t -> int -> int -> int -> int -> unit
end
type t = private Ojs.t
val of_element: Element.t -> t
[@@js.cast]
val getContext_2d: t -> RenderingContext2D.t
[@@js.custom
val get_context: t -> string -> Ojs.t
[@@js.call]
let getContext_2d x =
get_context x "2d"
]
end
module Document : sig
type t = private Ojs.t
val set_title: t -> string -> unit
val title: t -> string
val getElementById: t -> string -> Element.t
val getElementsByClassName: t -> string -> Element.t array
val createElement: t -> string -> Element.t
val createTextNode: t -> string -> Element.t
val body: t -> Element.t
end
module Window : sig
type t = private Ojs.t
val t_of_js: Ojs.t -> t
val t_to_js: t -> Ojs.t
val document: t -> Document.t
val set_onload: t -> (unit -> unit) -> unit
end
val window: Window.t
val alert: string -> unit
[@@js.global]
val setTimeout: (unit -> unit) -> int -> unit
module Console: sig
type t = private Ojs.t
val log: t -> Ojs.t -> unit
val log_string: t -> string -> unit
[@@js.call "log"]
end
val console: Console.t
module Person: sig
module Foo: sig
type t =
| Foo
| Bar [@js 42]
| OtherInt of int [@js.default]
| OtherString of string [@js.default]
[@@js.enum]
end
type t = private Ojs.t
val create: string -> Foo.t -> t
[@@js.new "Person"]
val name: t -> string
val foo: t -> Foo.t
val get: t -> unit -> string * Foo.t
[@@js.call]
val set: t -> string * Foo.t -> unit
[@@js.call]
val cast: t -> Ojs.t [@@js.cast]
end
module PersonObj: sig
class t: Ojs.t ->
object
inherit Ojs.obj
method name: string
method set_name: string -> unit
method foo: Person.Foo.t
method set_foo: Person.Foo.t -> unit
method get: string * Person.Foo.t [@@js.call]
method set: string * Person.Foo.t -> unit [@@js.call]
end
class person: string -> Person.Foo.t -> (int list [@js.variadic]) -> t
val create: string -> Person.Foo.t -> t
[@@js.new "Person"]
val of_person: Person.t -> t
[@@js.cast]
end
module Str: sig
class t: Ojs.t ->
object
inherit Ojs.obj
method concat: (t list [@js.variadic]) -> t
method to_string: string [@@js.call]
end
class str: string -> t [@@js.new "String"]
val create: string -> t
[@@js.new "String"]
end
module Date: sig
type t = private Ojs.t
val create: year:int -> month:int -> ?day:(int[@js.default 0]) -> unit -> t [@@js.new "Date"]
val to_string: t -> string [@@js.call]
end
module Person2: sig
type t = private Ojs.t
val mk: ?children:t list -> age:int -> (string[@js "name"]) -> t
[@@js.builder]
val to_json: t -> string
[@@js.global "JSON.stringify"]
end
type int_or_string_or_null =
| Int of int
| String of string
| Nothing
[@@js.union]
val f: ([`Int of int | `String of string | `Nothing] [@js.union]) -> unit
val g: int_or_string_or_null -> unit [@@js.global]
module Verb1: sig
type t1 =
{ x_coord: int;
y_coord: int;
}
class t2: Ojs.t ->
object
inherit Ojs.obj
method x_coord: int
method y_coord: int
end
end [@js.verbatim_names]
module Verb2: sig
type t1 =
{ x_coord: int;
y_coord: int;
} [@@js.verbatim_names]
class t2: Ojs.t ->
object
inherit Ojs.obj
method x_coord: int
method y_coord: int
end [@@js.verbatim_names]
end
module Console2: sig
val log: string -> unit
[@@js.global]
end [@js.scope "console"]
module Console3: sig
val log: 'a -> unit [@@js.global "console.log"]
val log2: 'a -> 'b -> unit [@@js.global "console.log"]
val log3: 'a -> 'b -> 'c -> unit [@@js.global "console.log"]
val log4: 'a -> 'b -> 'c -> 'd -> unit [@@js.global "console.log"]
end
module Console4: sig
val log: (module[@js] Ojs.T with type t = 'a) -> 'a -> unit [@@js.global "console.log"]
val log2:
(module[@js] Ojs.T with type t = 'a) ->
(module[@js] Ojs.T with type t = 'b) ->
'a -> 'b -> unit [@@js.global "console.log"]
val log3:
(module[@js] Ojs.T with type t = 'a) ->
(module[@js] Ojs.T with type t = 'b) ->
(module[@js] Ojs.T with type t = 'c) ->
'a -> 'b -> 'c -> unit [@@js.global "console.log"]
end
module Location: sig
val hash: unit -> string
val set_hash: string -> unit
end [@js.scope "location"]
module Location2: sig
val hash: unit -> string [@@js.get]
val set_hash: string -> unit [@@js.set]
end [@js.scope "location"]
module Location3: sig
val assign: string -> unit
val reload: ?force:bool -> unit -> unit
val replace: string -> unit
end [@js.scope "location"]
module Union: sig
type close_path
type moveto_abs
type svg_path_seg =
| Unknown of Ojs.t [@js.default]
| Close_path of close_path [@js 1]
| Moveto_abs of moveto_abs [@js 2]
[@@js.union on_field "pathSegType"]
end
module Ref : sig
type 'value t = private Ojs.t
val t_to_js: ('a -> Ojs.t) -> 'a t -> Ojs.t
val t_of_js: (Ojs.t -> 'a) -> Ojs.t -> 'a t
val make: 'value -> 'value t [@@js.global "makeRef"]
val current : 'value t -> 'value [@@js.get "current"]
val setCurrent : 'value t -> 'value -> unit [@@js.set "current"]
end
module Either : sig
type ('a, 'b) t
val t_to_js: ('a -> Ojs.t) -> ('b -> Ojs.t) -> ('a, 'b) t -> Ojs.t
val t_of_js: (Ojs.t -> 'a) -> (Ojs.t -> 'b) -> Ojs.t -> ('a, 'b) t
val left: 'a -> ('a, 'b) t [@@js.global "eitherLeft"]
val right: 'b -> ('a, 'b) t [@@js.global "eitherRight"]
val destruct: ('a, 'b) t -> left:('a -> 'c) -> right:('b -> 'c) -> 'c [@@js.global "eitherDestruct"]
end
module Alias : sig
module Swap : sig
type ('a, 'b) t = ('b, 'a) Either.t
val t_to_js: ('a -> Ojs.t) -> ('b -> Ojs.t) -> ('a, 'b) t -> Ojs.t
val t_of_js: (Ojs.t -> 'a) -> (Ojs.t -> 'b) -> Ojs.t -> ('a, 'b) t
end
(* Error: Contravariant type parameter !
module E : sig
type 'a t = 'a -> int
end *)
module Id : sig
type 'a t = 'a
val t_to_js: ('a -> Ojs.t) -> 'a t -> Ojs.t
val t_of_js: (Ojs.t -> 'a) -> Ojs.t -> 'a t
end
module Arrow : sig
type 'a t = ('a -> int) -> string
val t_to_js: ('a -> Ojs.t) -> 'a t -> Ojs.t
val t_of_js: (Ojs.t -> 'a) -> Ojs.t -> 'a t
end
module Record : sig
type ('a, 'b) t =
{
x: 'a;
y: 'b
}
val t_to_js: ('a -> Ojs.t) -> ('b -> Ojs.t) -> ('a, 'b) t -> Ojs.t
val t_of_js: (Ojs.t -> 'a) -> (Ojs.t -> 'b) -> Ojs.t -> ('a, 'b) t
end
end
module Variants : sig
module M1 : sig
type 'a t =
| X of 'a
| Y of int
[@@js.sum]
end
module M2 : sig
type ('a, 'b) t =
| X of 'a
| Y of 'b
[@@js.sum]
end
module M3 : sig
type 'a t =
| Empty
| Cons of 'a * 'a t
[@@js.sum]
val t_to_js: ('a -> Ojs.t) -> 'a t -> Ojs.t
val t_of_js: (Ojs.t -> 'a) -> Ojs.t -> 'a t
end
(* Error: Contravariant type parameter !
module E : sig
type 'a t =
| F of ('a -> int)
[@@js.sum]
end
*)
module M4 : sig
type 'a t =
| F of (('a -> int) -> int)
[@@js.sum]
end
end
================================================
FILE: gen_js_api.opam
================================================
# This file is generated by dune, edit dune-project instead
opam-version: "2.0"
version: "1.1.7"
synopsis: "Easy OCaml bindings for JavaScript libraries"
description: """
gen_js_api aims at simplifying the creation of OCaml bindings for
JavaScript libraries. Authors of bindings write OCaml signatures for
JavaScript libraries and the tool generates the actual binding code
with a combination of implicit conventions and explicit annotations.
gen_js_api is to be used with the js_of_ocaml compiler.
"""
maintainer: ["Alain Frisch <alain.frisch@lexifi.com>"]
authors: [
"Alain Frisch <alain.frisch@lexifi.com>"
"Sebastien Briais <sebastien.briais@lexifi.com>"
]
license: "MIT"
homepage: "https://github.com/LexiFi/gen_js_api"
bug-reports: "https://github.com/LexiFi/gen_js_api/issues"
depends: [
"dune" {>= "3.17"}
"ocaml" {>= "4.13"}
"ppxlib" {>= "0.37"}
"js_of_ocaml-compiler" {with-test}
"ojs" {= version}
"odoc" {with-doc}
]
conflicts: [
"js_of_ocaml-compiler" {< "6.3.0"}
]
build: [
["dune" "subst"] {dev}
[
"dune"
"build"
"-p"
name
"-j"
jobs
"@install"
"@runtest" {with-test}
"@doc" {with-doc}
]
]
dev-repo: "git+https://github.com/LexiFi/gen_js_api.git"
================================================
FILE: lib/dune
================================================
(library
(public_name ojs)
(synopsis "Runtime support for gen_js_api")
(libraries js_of_ocaml-compiler.runtime)
(wrapped false)
(foreign_stubs
(language c)
(names ojs_runtime_stubs))
(modes byte)
(js_of_ocaml
(javascript_files ojs_runtime.js)))
================================================
FILE: lib/ojs.ml
================================================
(* The gen_js_api is released under the terms of an MIT-like license. *)
(* See the attached LICENSE file. *)
(* Copyright 2015 by LexiFi. *)
(* This module (mostly) abstracts away from js_of_ocaml encoding of
OCaml values. It serves as a support library for the code generated
by gen_js_api.
The module could mostly be implemented on top of js_of_ocaml's Js module
(and in particular Js.Unsafe), but we prefer to drop the dependency
to js_of_ocaml's library and to rely only on its compiler and JS
runtime code.
*)
type t
external t_of_js: t -> t = "%identity"
external t_to_js: t -> t = "%identity"
external string_of_js: t -> string = "caml_js_to_string"
external string_to_js: string -> t = "caml_js_from_string"
external int_of_js: t -> int = "%identity"
external int_to_js: int -> t = "%identity"
external bool_of_js: t -> bool = "caml_js_to_bool"
external bool_to_js: bool -> t = "caml_js_from_bool"
external float_of_js: t -> float = "caml_js_to_float"
external float_to_js: float -> t = "caml_js_from_float"
external obj: (string * t) array -> t = "caml_js_object"
external variable: string -> t = "caml_js_var"
external get: t -> string -> t = "caml_js_get"
external set: t -> string -> t -> unit = "caml_js_set"
external delete: t -> string -> unit = "caml_js_delete"
external get_prop: t -> t -> t = "caml_js_get"
external set_prop: t -> t -> t -> unit = "caml_js_set"
external delete_prop: t -> t -> unit = "caml_js_delete"
external get_prop_ascii: t -> string -> t = "caml_js_get"
external set_prop_ascii: t -> string -> t -> unit = "caml_js_set"
external delete_prop_ascii: t -> string -> unit = "caml_js_delete"
external internal_type_of: t -> t = "caml_js_typeof"
let type_of x = string_of_js (internal_type_of x)
external internal_instance_of: t -> t -> t = "caml_js_instanceof"
let instance_of x ~constr = bool_of_js (internal_instance_of x constr)
external pure_js_expr: string -> t = "caml_pure_js_expr"
let null = pure_js_expr "null"
let undefined = pure_js_expr "undefined"
external equals: t -> t -> bool = "caml_js_equals"
let global = pure_js_expr "globalThis"
external new_obj: t -> t array -> t = "caml_js_new"
external call: t -> string -> t array -> t = "caml_js_meth_call"
external apply: t -> t array -> t = "caml_js_fun_call"
let array_make n = new_obj (get_prop_ascii global "Array") [|int_to_js n|]
let array_get t i = get_prop t (int_to_js i)
let array_set t i x = set_prop t (int_to_js i) x
let array_of_js_from f objs start =
let n = int_of_js (get_prop_ascii objs "length") in
Array.init (n - start) (fun i -> f (array_get objs (start + i)))
let array_of_js f objs = array_of_js_from f objs 0
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
let list_of_js_from f objs start = Array.to_list (array_of_js_from f objs start)
let list_of_js f objs = list_of_js_from f objs 0
let list_to_js f l =
array_to_js f (Array.of_list l)
let option_of_js f x =
if equals x null then None
else Some (f x)
let option_to_js f = function
| Some x -> f x
| None -> null
let unit_to_js () = undefined
let unit_of_js _ = ()
class obj (x:t) =
object
method to_js = x
end
external fun_to_js: int -> (t -> 'a) -> t = "caml_js_wrap_callback_strict"
external fun_to_js_args: (t -> 'a) -> t = "caml_ojs_wrap_fun_arguments"
let has_property o x =
type_of o = "object" && o != null
&& get_prop o (string_to_js x) != undefined
external new_obj_arr: t -> t -> t = "caml_ojs_new_arr"
let empty_obj () = new_obj (get_prop_ascii global "Object") [||]
external iter_properties_untyped : t -> t -> unit = "caml_ojs_iterate_properties"
let iter_properties x f =
iter_properties_untyped x (fun_to_js 1 (fun x -> f (string_of_js x)))
let apply_arr o arr = call o "apply" [| null; arr |]
let call_arr o s arr = call (get_prop o (string_to_js s)) "apply" [| o; arr |]
let is_null x =
equals x null
let obj_type x =
string_of_js (call (pure_js_expr "Object.prototype.toString") "call" [|x|])
module type T = sig
type js := t
type t
val t_to_js : t -> js
val t_of_js : js -> t
end
(* Ojs.T instances for built-in types *)
module Int = struct
type t = int
let t_to_js = int_to_js
let t_of_js = int_of_js
end
module String = struct
type t = string
let t_to_js = string_to_js
let t_of_js = string_of_js
end
module Bool = struct
type t = bool
let t_to_js = bool_to_js
let t_of_js = bool_of_js
end
module Float = struct
type t = float
let t_to_js = float_to_js
let t_of_js = float_of_js
end
module Array (A: T) = struct
type t = A.t array
let t_to_js = array_to_js A.t_to_js
let t_of_js = array_of_js A.t_of_js
end
module List (A: T) = struct
type t = A.t list
let t_to_js = list_to_js A.t_to_js
let t_of_js = list_of_js A.t_of_js
end
module Option (A: T) = struct
type t = A.t option
let t_to_js = option_to_js A.t_to_js
let t_of_js = option_of_js A.t_of_js
end
================================================
FILE: lib/ojs.mli
================================================
(* The gen_js_api is released under the terms of an MIT-like license. *)
(* See the attached LICENSE file. *)
(* Copyright 2015 by LexiFi. *)
(** Binding with JS values. *)
type t
(** The universal type representing arbitrary JS values. *)
(** {2 Mapper for built-in types} *)
external t_of_js: t -> t = "%identity"
external t_to_js: t -> t = "%identity"
external string_of_js: t -> string = "caml_js_to_string"
external string_to_js: string -> t = "caml_js_from_string"
external int_of_js: t -> int = "%identity"
external int_to_js: int -> t = "%identity"
external bool_of_js: t -> bool = "caml_js_to_bool"
external bool_to_js: bool -> t = "caml_js_from_bool"
external float_of_js: t -> float = "caml_js_to_float"
external float_to_js: float -> t = "caml_js_from_float"
val array_of_js: (t -> 'a) -> t -> 'a array
val array_to_js: ('a -> t) -> 'a array -> t
val list_of_js: (t -> 'a) -> t -> 'a list
val list_to_js: ('a -> t) -> 'a list -> t
val array_of_js_from: (t -> 'a) -> t -> int -> 'a array
val list_of_js_from: (t -> 'a) -> t -> int -> 'a list
val option_of_js: (t -> 'a) -> t -> 'a option
(** Both [null] and [undefined] are mapped to [None]. *)
val option_to_js: ('a -> t) -> 'a option -> t
(** [None] is mapped to [null]. *)
val unit_of_js: t -> unit
val unit_to_js: unit -> t
(** {2 Wrap OCaml functions as JS functions} *)
external fun_to_js: int -> (t -> 'a) -> t = "caml_js_wrap_callback_strict"
(** Wrap an OCaml function of known arity (>=1) into a JS function.
Extra arguments are discarded and missing argument are filled with
'undefined'.
*)
external fun_to_js_args: (t -> 'a) -> t = "caml_ojs_wrap_fun_arguments"
(** Wrap an OCaml function taking JS arguments as a JS array. *)
(** {2 JS objects} *)
external get: t -> string -> t = "caml_js_get"
[@@ocaml.deprecated "Use Ojs.get_prop_ascii instead."]
external set: t -> string -> t -> unit = "caml_js_set"
[@@ocaml.deprecated "Use Ojs.set_prop_ascii instead."]
external delete: t -> string -> unit = "caml_js_delete"
[@@ocaml.deprecated "Use Ojs.delete_prop_ascii instead."]
external get_prop_ascii: t -> string -> t = "caml_js_get"
(** Get the property from an object (only works if the property key is a plain ascii string). *)
external set_prop_ascii: t -> string -> t -> unit = "caml_js_set"
(** Set an object property (only works if the property key is a plain ascii string). *)
external delete_prop_ascii: t -> string -> unit = "caml_js_delete"
(** Delete an object property (only works if the property key is a plain ascii string). *)
external get_prop: t -> t -> t = "caml_js_get"
(** Get the property from an object. *)
external set_prop: t -> t -> t -> unit = "caml_js_set"
(** Set an object property. *)
external delete_prop: t -> t -> unit = "caml_js_delete"
(** Delete an object property. *)
external obj: (string * t) array -> t = "caml_js_object"
val empty_obj: unit -> t
val has_property: t -> string -> bool
val iter_properties: t -> (string -> unit) -> unit
(** {2 Calling JS functions} *)
external call: t -> string -> t array -> t = "caml_js_meth_call"
(** Call a method on an object (binding 'this' to the object). *)
external apply: t -> t array -> t = "caml_js_fun_call"
(** Call a function. *)
external new_obj: t -> t array -> t = "caml_js_new"
(** Call a constructor *)
val call_arr: t -> string -> t -> t
(** Variant of [Ojs.call] where the arguments are passed as an already
built JS array. *)
val apply_arr: t -> t -> t
(** Variant of [Ojs.apply] where the arguments are passed as an already
built JS array. *)
external new_obj_arr: t -> t -> t = "caml_ojs_new_arr"
(** Variant of [Ojs.new_obj] where the arguments are passed as an already
built JS array. *)
(** {2 Arrays} *)
val array_make: int -> t
val array_get: t -> int -> t
val array_set: t -> int -> t -> unit
(** {2 Misc} *)
val global: t
val null: t
external variable: string -> t = "caml_js_var"
val type_of: t -> string
val instance_of: t -> constr:t -> bool
class obj: t ->
object
method to_js: t
end
val is_null: t -> bool
val obj_type: t -> string
(** Returns:
"[object Array]"
"[object Object]"
"[object Number]"
"[object String]"
"[object Null]"
"[object Boolean]"
*)
module type T =
sig
type js := t
type t
val t_to_js : t -> js
val t_of_js : js -> t
end
(* Ojs.T instances for built-in types *)
module Int : T with type t = int
module String : T with type t = string
module Bool : T with type t = bool
module Float : T with type t = float
module Array (A: T) : T with type t = A.t array
module List (A: T) : T with type t = A.t list
module Option (A: T) : T with type t = A.t option
================================================
FILE: lib/ojs_exn.ml
================================================
(* The gen_js_api is released under the terms of an MIT-like license. *)
(* See the attached LICENSE file. *)
(* Copyright 2015 by LexiFi. *)
type t = Jsoo_runtime.Error.t
external coerce : t -> Ojs.t = "%identity"
let name x = Ojs.string_of_js (Ojs.get_prop_ascii (coerce x) "name")
let message x = Ojs.string_of_js (Ojs.get_prop_ascii (coerce x) "message")
let stack x = Ojs.option_of_js Ojs.string_of_js (Ojs.get_prop_ascii (coerce x) "stack")
let to_string x = Ojs.string_of_js (Ojs.call (coerce x) "toString" [||])
exception Error = Jsoo_runtime.Error.Exn
let () =
Printexc.register_printer (function
| Error x -> Some (to_string x)
| _ -> None
)
================================================
FILE: lib/ojs_exn.mli
================================================
(* The gen_js_api is released under the terms of an MIT-like license. *)
(* See the attached LICENSE file. *)
(* Copyright 2015 by LexiFi. *)
(** OCaml view on JS exceptions *)
type t
val name: t -> string
val message: t -> string
val stack: t -> string option
val to_string: t -> string
exception Error of t
================================================
FILE: lib/ojs_runtime.js
================================================
//Provides: caml_ojs_wrap_fun_arguments
//Requires: caml_js_wrap_callback
function caml_ojs_wrap_fun_arguments(f) {
return function() {
return caml_js_wrap_callback(f)(arguments);
}
}
//Provides: caml_ojs_iterate_properties
//Requires: caml_js_to_string
function caml_ojs_iterate_properties(o, f) {
var name;
for(name in o) {
if(o.hasOwnProperty(name)) {
f(name);
}
}
}
================================================
FILE: lib/ojs_runtime_stubs.c
================================================
#include <stdlib.h>
#include <stdio.h>
void caml_ojs_wrap_fun_arguments () {
fprintf(stderr, "Unimplemented JavaScript primitive caml_ojs_wrap_fun_arguments!\n");
exit(1);
}
void caml_ojs_iterate_properties () {
fprintf(stderr, "Unimplemented JavaScript primitive caml_ojs_iterate_properties!\n");
exit(1);
}
================================================
FILE: node-test/bindings/arrays.mli
================================================
module JsArray (E: Ojs.T): sig
type t
val t_to_js: t -> Ojs.t
val t_of_js: Ojs.t -> t
val create: unit -> t [@@js.new "Array"]
val push: t -> E.t -> unit [@@js.call]
val pop: t -> E.t option [@@js.call]
end
module UntypedArray : sig
include (module type of JsArray(Ojs))
end
module StringArray : sig
include (module type of JsArray(Ojs.String))
val join: t -> string -> string [@@js.call]
end
module[@js.scope "Array"] JsArray2: sig
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
val create: unit -> 'a t [@@js.create]
val create': (module[@js] Ojs.T with type t = 'a) -> ('a list [@js.variadic]) -> 'a t [@@js.create]
val push: (module[@js] Ojs.T with type t = 'a) -> 'a t -> 'a -> unit [@@js.call]
val pop: (module[@js] Ojs.T with type t = 'a) -> 'a t -> 'a option [@@js.call]
val get: (module[@js] Ojs.T with type t = 'a) -> 'a t -> int -> 'a option [@@js.index_get]
val set: (module[@js] Ojs.T with type t = 'a) -> 'a t -> int -> 'a -> unit [@@js.index_set]
val join: string t -> string -> string [@@js.call]
end
================================================
FILE: node-test/bindings/buffer.mli
================================================
[@@@js.scope "Buffer"]
type t = private Ojs.t
val t_of_js: Ojs.t -> t
val t_to_js: t -> Ojs.t
val alloc: int -> t[@@js.global]
val from: string -> t[@@js.global]
val concat: t list -> t[@@js.global]
val length: t -> int [@@js.get]
val get: t -> int -> int option [@@js.index_get]
val set: t -> int -> int -> unit[@@js.index_set]
val write: t -> string -> int[@@js.call]
val slice: t -> int -> int -> t[@@js.call]
val to_string: t -> string[@@js.call]
val copy: t -> dst:t -> start:int -> dst_start:int -> dst_end:int -> int[@@js.call]
================================================
FILE: node-test/bindings/console.mli
================================================
[@@@js.scope "console"]
val log: 'a -> unit [@@js.global]
val error: 'a -> unit [@@js.global]
module T : sig
val log: (module[@js] Ojs.T with type t = 'a) -> 'a -> unit [@@js.global]
val error: (module[@js] Ojs.T with type t = 'a) -> 'a -> unit [@@js.global]
end
================================================
FILE: node-test/bindings/container.ml
================================================
module StringMap = struct
include Map.Make(String)
let t_to_js ml2js l =
let o = Ojs.empty_obj () in
iter (fun k v -> Ojs.set_prop o (Ojs.string_to_js k) (ml2js v)) l;
o
let t_of_js js2ml o =
let l = ref empty in
Ojs.iter_properties o
(fun k -> l := add k (js2ml (Ojs.get_prop o (Ojs.string_to_js k))) !l);
!l
end
================================================
FILE: node-test/bindings/container.mli
================================================
module StringMap : sig
include Map.S with type key = string
val t_to_js: ('a -> Ojs.t) -> 'a t -> Ojs.t
val t_of_js: (Ojs.t -> 'a) -> Ojs.t -> 'a t
end
================================================
FILE: node-test/bindings/dune
================================================
(library
(name node)
(synopsis "Bindings")
(libraries ojs)
(preprocess
(pps gen_js_api.ppx))
(modes byte)
(js_of_ocaml
(javascript_files imports.js))
(wasm_of_ocaml
(javascript_files imports.js imports.wat)))
(rule
(targets imports.ml)
(deps imports.mli)
(action
(run gen_js_api %{deps})))
(rule
(alias runtest)
(action
(diff expected/imports.ml imports.ml)))
(rule
(targets errors.ml)
(deps errors.mli)
(action
(run gen_js_api %{deps})))
(rule
(alias runtest)
(action
(diff expected/errors.ml errors.ml)))
(rule
(targets global.ml)
(deps global.mli)
(action
(run gen_js_api %{deps})))
(rule
(alias runtest)
(action
(diff expected/global.ml global.ml)))
(rule
(targets promise.ml)
(deps promise.mli)
(action
(run gen_js_api %{deps})))
(rule
(alias runtest)
(action
(diff expected/promise.ml promise.ml)))
(rule
(targets buffer.ml)
(deps buffer.mli)
(action
(run gen_js_api %{deps})))
(rule
(alias runtest)
(action
(diff expected/buffer.ml buffer.ml)))
(rule
(targets fs.ml)
(deps fs.mli)
(action
(run gen_js_api %{deps})))
(rule
(alias runtest)
(action
(diff expected/fs.ml fs.ml)))
(rule
(targets path.ml)
(deps path.mli)
(action
(run gen_js_api %{deps})))
(rule
(alias runtest)
(action
(diff expected/path.ml path.ml)))
(rule
(targets process.ml)
(deps process.mli)
(action
(run gen_js_api %{deps})))
(rule
(alias runtest)
(action
(diff expected/process.ml process.ml)))
(rule
(targets console.ml)
(deps console.mli)
(action
(run gen_js_api %{deps})))
(rule
(alias runtest)
(action
(diff expected/console.ml console.ml)))
(rule
(targets arrays.ml)
(deps arrays.mli)
(action
(run gen_js_api %{deps})))
(rule
(alias runtest)
(action
(diff expected/arrays.ml arrays.ml)))
(rule
(targets number.ml)
(deps number.mli)
(action
(run gen_js_api %{deps})))
(rule
(alias runtest)
(action
(diff expected/number.ml number.ml)))
================================================
FILE: node-test/bindings/errors.mli
================================================
module [@js.scope] Error : sig
type t
val t_to_js: t -> Ojs.t
val t_of_js: Ojs.t -> t
val create: string -> t [@@js.create]
val stack_trace_limit: int [@@js.global]
val set_stack_trace_limit: int -> unit [@@js.set]
val code: t -> string [@@js.get]
val message: t -> string [@@js.get]
val stack: t -> string [@@js.get]
end
================================================
FILE: node-test/bindings/expected/arrays.ml
================================================
[@@@js.dummy "!! This code has been generated by gen_js_api !!"]
[@@@ocaml.warning "-7-32-39"]
module JsArray(E:Ojs.T) =
struct
type t = Ojs.t
let rec t_of_js : Ojs.t -> t = fun (x2 : Ojs.t) -> x2
and t_to_js : t -> Ojs.t = fun (x1 : Ojs.t) -> x1
let create : unit -> t =
fun () ->
t_of_js (Ojs.new_obj (Ojs.get_prop_ascii Ojs.global "Array") [||])
let push : t -> E.t -> unit =
fun (x4 : t) (x3 : E.t) ->
ignore (Ojs.call (t_to_js x4) "push" [|(E.t_to_js x3)|])
let pop : t -> E.t option =
fun (x5 : t) ->
Ojs.option_of_js E.t_of_js (Ojs.call (t_to_js x5) "pop" [||])
end
module UntypedArray = struct include (JsArray)(Ojs) end
module StringArray =
struct
include (JsArray)(Ojs.String)
let join : t -> string -> string =
fun (x8 : t) (x7 : string) ->
Ojs.string_of_js
(Ojs.call (t_to_js x8) "join" [|(Ojs.string_to_js x7)|])
end
module JsArray2 =
struct
type 'a t = Ojs.t
let rec t_of_js : 'a . (Ojs.t -> 'a) -> Ojs.t -> 'a t =
fun (type __a) (__a_of_js : Ojs.t -> __a) -> fun (x10 : Ojs.t) -> x10
and t_to_js : 'a . ('a -> Ojs.t) -> 'a t -> Ojs.t =
fun (type __a) (__a_to_js : __a -> Ojs.t) -> fun (x9 : Ojs.t) -> x9
let create : unit -> 'a t =
fun () ->
t_of_js Obj.magic
(Ojs.new_obj (Ojs.get_prop_ascii Ojs.global "Array") [||])
let create' : (module Ojs.T with type t = 'a) -> 'a list -> 'a t =
fun (type a) ->
fun ((module A) : (module Ojs.T with type t = a)) (x12 : a list) ->
t_of_js A.t_of_js
(Ojs.new_obj_arr (Ojs.get_prop_ascii Ojs.global "Array")
(let x13 =
Ojs.new_obj (Ojs.get_prop_ascii Ojs.global "Array") [||] in
List.iter
(fun (x14 : a) ->
ignore (Ojs.call x13 "push" [|(A.t_to_js x14)|])) x12;
x13))
let push : (module Ojs.T with type t = 'a) -> 'a t -> 'a -> unit =
fun (type a) ->
fun ((module A) : (module Ojs.T with type t = a)) (x17 : a t)
(x16 : a) ->
ignore
(Ojs.call (t_to_js A.t_to_js x17) "push" [|(A.t_to_js x16)|])
let pop : (module Ojs.T with type t = 'a) -> 'a t -> 'a option =
fun (type a) ->
fun ((module A) : (module Ojs.T with type t = a)) (x19 : a t) ->
Ojs.option_of_js A.t_of_js
(Ojs.call (t_to_js A.t_to_js x19) "pop" [||])
let get : (module Ojs.T with type t = 'a) -> 'a t -> int -> 'a option =
fun (type a) ->
fun ((module A) : (module Ojs.T with type t = a)) (x22 : a t)
(x24 : int) ->
Ojs.option_of_js A.t_of_js
(Ojs.array_get (t_to_js A.t_to_js x22) x24)
let set : (module Ojs.T with type t = 'a) -> 'a t -> int -> 'a -> unit =
fun (type a) ->
fun ((module A) : (module Ojs.T with type t = a)) (x26 : a t)
(x28 : int) (x29 : a) ->
Ojs.array_set (t_to_js A.t_to_js x26) x28 (A.t_to_js x29)
let join : string t -> string -> string =
fun (x31 : string t) (x30 : string) ->
Ojs.string_of_js
(Ojs.call (t_to_js Ojs.string_to_js x31) "join"
[|(Ojs.string_to_js x30)|])
end
================================================
FILE: node-test/bindings/expected/buffer.ml
================================================
[@@@js.dummy "!! This code has been generated by gen_js_api !!"]
[@@@ocaml.warning "-7-32-39"]
type t = Ojs.t
let rec t_of_js : Ojs.t -> t = fun (x2 : Ojs.t) -> x2
and t_to_js : t -> Ojs.t = fun (x1 : Ojs.t) -> x1
let alloc : int -> t =
fun (x3 : int) ->
t_of_js
(Ojs.call (Ojs.get_prop_ascii Ojs.global "Buffer") "alloc"
[|(Ojs.int_to_js x3)|])
let from : string -> t =
fun (x4 : string) ->
t_of_js
(Ojs.call (Ojs.get_prop_ascii Ojs.global "Buffer") "from"
[|(Ojs.string_to_js x4)|])
let concat : t list -> t =
fun (x5 : t list) ->
t_of_js
(Ojs.call (Ojs.get_prop_ascii Ojs.global "Buffer") "concat"
[|(Ojs.list_to_js t_to_js x5)|])
let length : t -> int =
fun (x7 : t) -> Ojs.int_of_js (Ojs.get_prop_ascii (t_to_js x7) "length")
let get : t -> int -> int option =
fun (x8 : t) (x9 : int) ->
Ojs.option_of_js Ojs.int_of_js (Ojs.array_get (t_to_js x8) x9)
let set : t -> int -> int -> unit =
fun (x11 : t) (x12 : int) (x13 : int) ->
Ojs.array_set (t_to_js x11) x12 (Ojs.int_to_js x13)
let write : t -> string -> int =
fun (x15 : t) (x14 : string) ->
Ojs.int_of_js (Ojs.call (t_to_js x15) "write" [|(Ojs.string_to_js x14)|])
let slice : t -> int -> int -> t =
fun (x18 : t) (x16 : int) (x17 : int) ->
t_of_js
(Ojs.call (t_to_js x18) "slice"
[|(Ojs.int_to_js x16);(Ojs.int_to_js x17)|])
let to_string : t -> string =
fun (x19 : t) -> Ojs.string_of_js (Ojs.call (t_to_js x19) "toString" [||])
let copy : t -> dst:t -> start:int -> dst_start:int -> dst_end:int -> int =
fun (x24 : t) ~dst:(x20 : t) ~start:(x21 : int) ~dst_start:(x22 : int)
~dst_end:(x23 : int) ->
Ojs.int_of_js
(Ojs.call (t_to_js x24) "copy"
[|(t_to_js x20);(Ojs.int_to_js x21);(Ojs.int_to_js x22);(Ojs.int_to_js
x23)|])
================================================
FILE: node-test/bindings/expected/console.ml
================================================
[@@@js.dummy "!! This code has been generated by gen_js_api !!"]
[@@@ocaml.warning "-7-32-39"]
let log : 'a -> unit =
fun (x1 : 'a) ->
ignore
(Ojs.call (Ojs.get_prop_ascii Ojs.global "console") "log"
[|(Obj.magic x1)|])
let error : 'a -> unit =
fun (x2 : 'a) ->
ignore
(Ojs.call (Ojs.get_prop_ascii Ojs.global "console") "error"
[|(Obj.magic x2)|])
module T =
struct
let log : (module Ojs.T with type t = 'a) -> 'a -> unit =
fun (type a) ->
fun ((module A) : (module Ojs.T with type t = a)) (x3 : a) ->
ignore
(Ojs.call (Ojs.get_prop_ascii Ojs.global "console") "log"
[|(A.t_to_js x3)|])
let error : (module Ojs.T with type t = 'a) -> 'a -> unit =
fun (type a) ->
fun ((module A) : (module Ojs.T with type t = a)) (x4 : a) ->
ignore
(Ojs.call (Ojs.get_prop_ascii Ojs.global "console") "error"
[|(A.t_to_js x4)|])
end
================================================
FILE: node-test/bindings/expected/errors.ml
================================================
[@@@js.dummy "!! This code has been generated by gen_js_api !!"]
[@@@ocaml.warning "-7-32-39"]
module Error =
struct
type t = Ojs.t
let rec t_of_js : Ojs.t -> t = fun (x2 : Ojs.t) -> x2
and t_to_js : t -> Ojs.t = fun (x1 : Ojs.t) -> x1
let create : string -> t =
fun (x3 : string) ->
t_of_js
(Ojs.new_obj (Ojs.get_prop_ascii Ojs.global "Error")
[|(Ojs.string_to_js x3)|])
let stack_trace_limit : int =
Ojs.int_of_js
(Ojs.get_prop_ascii (Ojs.get_prop_ascii Ojs.global "Error")
"stackTraceLimit")
let set_stack_trace_limit : int -> unit =
fun (x4 : int) ->
Ojs.set_prop_ascii (Ojs.get_prop_ascii Ojs.global "Error")
"stackTraceLimit" (Ojs.int_to_js x4)
let code : t -> string =
fun (x5 : t) ->
Ojs.string_of_js (Ojs.get_prop_ascii (t_to_js x5) "code")
let message : t -> string =
fun (x6 : t) ->
Ojs.string_of_js (Ojs.get_prop_ascii (t_to_js x6) "message")
let stack : t -> string =
fun (x7 : t) ->
Ojs.string_of_js (Ojs.get_prop_ascii (t_to_js x7) "stack")
end
================================================
FILE: node-test/bindings/expected/fs.ml
================================================
[@@@js.dummy "!! This code has been generated by gen_js_api !!"]
[@@@ocaml.warning "-7-32-39"]
module Dirent =
struct
type t = Ojs.t
let rec t_of_js : Ojs.t -> t = fun (x2 : Ojs.t) -> x2
and t_to_js : t -> Ojs.t = fun (x1 : Ojs.t) -> x1
let name : t -> string =
fun (x3 : t) ->
Ojs.string_of_js (Ojs.get_prop_ascii (t_to_js x3) "name")
let is_file : t -> bool =
fun (x4 : t) -> Ojs.bool_of_js (Ojs.call (t_to_js x4) "isFile" [||])
let is_directory : t -> bool =
fun (x5 : t) ->
Ojs.bool_of_js (Ojs.call (t_to_js x5) "isDirectory" [||])
end
module Dir =
struct
type t = Ojs.t
let rec t_of_js : Ojs.t -> t = fun (x7 : Ojs.t) -> x7
and t_to_js : t -> Ojs.t = fun (x6 : Ojs.t) -> x6
let path : t -> string =
fun (x8 : t) ->
Ojs.string_of_js (Ojs.get_prop_ascii (t_to_js x8) "path")
let close : t -> unit Promise.t =
fun (x9 : t) ->
Promise.t_of_js Ojs.unit_of_js (Ojs.call (t_to_js x9) "close" [||])
let read : t -> Dirent.t option Promise.t =
fun (x11 : t) ->
Promise.t_of_js
(fun (x12 : Ojs.t) -> Ojs.option_of_js Dirent.t_of_js x12)
(Ojs.call (t_to_js x11) "read" [||])
end
module FileHandle =
struct
type t = Ojs.t
let rec t_of_js : Ojs.t -> t = fun (x15 : Ojs.t) -> x15
and t_to_js : t -> Ojs.t = fun (x14 : Ojs.t) -> x14
type read = {
bytes_read: int ;
buffer: Buffer.t }
let rec read_of_js : Ojs.t -> read =
fun (x17 : Ojs.t) ->
{
bytes_read = (Ojs.int_of_js (Ojs.get_prop_ascii x17 "bytesRead"));
buffer = (Buffer.t_of_js (Ojs.get_prop_ascii x17 "buffer"))
}
and read_to_js : read -> Ojs.t =
fun (x16 : read) ->
Ojs.obj
[|("bytesRead", (Ojs.int_to_js x16.bytes_read));("buffer",
(Buffer.t_to_js
x16.buffer))|]
let append_file : t -> Buffer.t -> unit Promise.t =
fun (x19 : t) (x18 : Buffer.t) ->
Promise.t_of_js Ojs.unit_of_js
(Ojs.call (t_to_js x19) "appendFile" [|(Buffer.t_to_js x18)|])
let read : t -> Buffer.t -> int -> int -> int -> read Promise.t =
fun (x25 : t) (x21 : Buffer.t) (x22 : int) (x23 : int) (x24 : int) ->
Promise.t_of_js read_of_js
(Ojs.call (t_to_js x25) "read"
[|(Buffer.t_to_js x21);(Ojs.int_to_js x22);(Ojs.int_to_js x23);(
Ojs.int_to_js x24)|])
let chmod : t -> int -> unit Promise.t =
fun (x28 : t) (x27 : int) ->
Promise.t_of_js Ojs.unit_of_js
(Ojs.call (t_to_js x28) "chmod" [|(Ojs.int_to_js x27)|])
let chmown : t -> uid:int -> gid:int -> unit Promise.t =
fun (x32 : t) ~uid:(x30 : int) ~gid:(x31 : int) ->
Promise.t_of_js Ojs.unit_of_js
(Ojs.call (t_to_js x32) "chmown"
[|(Ojs.int_to_js x30);(Ojs.int_to_js x31)|])
let close : t -> unit Promise.t =
fun (x34 : t) ->
Promise.t_of_js Ojs.unit_of_js (Ojs.call (t_to_js x34) "close" [||])
let datasync : t -> unit Promise.t =
fun (x36 : t) ->
Promise.t_of_js Ojs.unit_of_js
(Ojs.call (t_to_js x36) "datasync" [||])
let fd : t -> int =
fun (x38 : t) -> Ojs.int_of_js (Ojs.get_prop_ascii (t_to_js x38) "fd")
end
let readdir : string -> string list Promise.t =
fun (x39 : string) ->
Promise.t_of_js
(fun (x40 : Ojs.t) -> Ojs.list_of_js Ojs.string_of_js x40)
(Ojs.call
(Ojs.get_prop_ascii (Jsoo_runtime.Js.runtime_value "node_fs")
"promises") "readdir" [|(Ojs.string_to_js x39)|])
let open_ : string -> flag:string -> FileHandle.t Promise.t =
fun (x42 : string) ~flag:(x43 : string) ->
Promise.t_of_js FileHandle.t_of_js
(Ojs.call
(Ojs.get_prop_ascii (Jsoo_runtime.Js.runtime_value "node_fs")
"promises") "open"
[|(Ojs.string_to_js x42);(Ojs.string_to_js x43)|])
let rmdir : string -> unit Promise.t =
fun (x45 : string) ->
Promise.t_of_js Ojs.unit_of_js
(Ojs.call
(Ojs.get_prop_ascii (Jsoo_runtime.Js.runtime_value "node_fs")
"promises") "rmdir" [|(Ojs.string_to_js x45)|])
let rename : string -> string -> unit Promise.t =
fun (x47 : string) (x48 : string) ->
Promise.t_of_js Ojs.unit_of_js
(Ojs.call
(Ojs.get_prop_ascii (Jsoo_runtime.Js.runtime_value "node_fs")
"promises") "rename"
[|(Ojs.string_to_js x47);(Ojs.string_to_js x48)|])
let unlink : string -> unit Promise.t =
fun (x50 : string) ->
Promise.t_of_js Ojs.unit_of_js
(Ojs.call
(Ojs.get_prop_ascii (Jsoo_runtime.Js.runtime_value "node_fs")
"promises") "unlink" [|(Ojs.string_to_js x50)|])
================================================
FILE: node-test/bindings/expected/global.ml
================================================
[@@@js.dummy "!! This code has been generated by gen_js_api !!"]
[@@@ocaml.warning "-7-32-39"]
type timeout_id = Ojs.t
let rec timeout_id_of_js : Ojs.t -> timeout_id = fun (x2 : Ojs.t) -> x2
and timeout_id_to_js : timeout_id -> Ojs.t = fun (x1 : Ojs.t) -> x1
type interval_id = Ojs.t
let rec interval_id_of_js : Ojs.t -> interval_id = fun (x4 : Ojs.t) -> x4
and interval_id_to_js : interval_id -> Ojs.t = fun (x3 : Ojs.t) -> x3
let set_interval : (unit -> unit) -> int -> interval_id =
fun (x5 : unit -> unit) (x6 : int) ->
interval_id_of_js
(Ojs.call Ojs.global "setInterval"
[|(Ojs.fun_to_js 1 (fun _ -> x5 ()));(Ojs.int_to_js x6)|])
let set_timeout : (unit -> unit) -> int -> timeout_id =
fun (x7 : unit -> unit) (x8 : int) ->
timeout_id_of_js
(Ojs.call Ojs.global "setTimeout"
[|(Ojs.fun_to_js 1 (fun _ -> x7 ()));(Ojs.int_to_js x8)|])
let clear_timeout : timeout_id -> unit =
fun (x9 : timeout_id) ->
ignore (Ojs.call Ojs.global "clearTimeout" [|(timeout_id_to_js x9)|])
let clear_interval : interval_id -> unit =
fun (x10 : interval_id) ->
ignore (Ojs.call Ojs.global "clearInterval" [|(interval_id_to_js x10)|])
================================================
FILE: node-test/bindings/expected/imports.ml
================================================
[@@@js.dummy "!! This code has been generated by gen_js_api !!"]
[@@@ocaml.warning "-7-32-39"]
let path : Ojs.t = Jsoo_runtime.Js.runtime_value "node_path"
================================================
FILE: node-test/bindings/expected/number.ml
================================================
[@@@js.dummy "!! This code has been generated by gen_js_api !!"]
[@@@ocaml.warning "-7-32-39"]
type t = Ojs.t
let rec t_of_js : Ojs.t -> t = fun (x2 : Ojs.t) -> x2
and t_to_js : t -> Ojs.t = fun (x1 : Ojs.t) -> x1
let toString : t -> ?radix:int -> unit -> float =
fun (x6 : t) ?radix:(x3 : int option) () ->
Ojs.float_of_js
(let x7 = t_to_js x6 in
Ojs.call (Ojs.get_prop_ascii x7 "toString") "apply"
[|x7;((let x4 =
Ojs.new_obj (Ojs.get_prop_ascii Ojs.global "Array") [||] in
(match x3 with
| Some x5 ->
ignore (Ojs.call x4 "push" [|(Ojs.int_to_js x5)|])
| None -> ());
x4))|])
let toFixed : t -> ?fractionDigits:int -> unit -> float =
fun (x11 : t) ?fractionDigits:(x8 : int option) () ->
Ojs.float_of_js
(let x12 = t_to_js x11 in
Ojs.call (Ojs.get_prop_ascii x12 "toFixed") "apply"
[|x12;((let x9 =
Ojs.new_obj (Ojs.get_prop_ascii Ojs.global "Array") [||] in
(match x8 with
| Some x10 ->
ignore (Ojs.call x9 "push" [|(Ojs.int_to_js x10)|])
| None -> ());
x9))|])
let toExponential : t -> ?fractionDigits:int -> unit -> float =
fun (x16 : t) ?fractionDigits:(x13 : int option) () ->
Ojs.float_of_js
(let x17 = t_to_js x16 in
Ojs.call (Ojs.get_prop_ascii x17 "toExponential") "apply"
[|x17;((let x14 =
Ojs.new_obj (Ojs.get_prop_ascii Ojs.global "Array") [||] in
(match x13 with
| Some x15 ->
ignore (Ojs.call x14 "push" [|(Ojs.int_to_js x15)|])
| None -> ());
x14))|])
let toPrecision : t -> ?precision:int -> unit -> float =
fun (x21 : t) ?precision:(x18 : int option) () ->
Ojs.float_of_js
(let x22 = t_to_js x21 in
Ojs.call (Ojs.get_prop_ascii x22 "toPrecision") "apply"
[|x22;((let x19 =
Ojs.new_obj (Ojs.get_prop_ascii Ojs.global "Array") [||] in
(match x18 with
| Some x20 ->
ignore (Ojs.call x19 "push" [|(Ojs.int_to_js x20)|])
| None -> ());
x19))|])
let valueOf : t -> float =
fun (x23 : t) -> Ojs.float_of_js (Ojs.call (t_to_js x23) "valueOf" [||])
module Scoped =
struct
let create : 'any -> t =
fun (x24 : 'any) ->
t_of_js
(Ojs.new_obj (Ojs.get_prop_ascii Ojs.global "Number")
[|(Obj.magic x24)|])
let invoke : 'any -> float =
fun (x25 : 'any) ->
Ojs.float_of_js
(Ojs.apply (Ojs.get_prop_ascii Ojs.global "Number")
[|(Obj.magic x25)|])
let min_value : float =
Ojs.float_of_js
(Ojs.get_prop_ascii (Ojs.get_prop_ascii Ojs.global "Number")
"MIN_VALUE")
let max_value : float =
Ojs.float_of_js
(Ojs.get_prop_ascii (Ojs.get_prop_ascii Ojs.global "Number")
"MAX_VALUE")
let nan : float =
Ojs.float_of_js
(Ojs.get_prop_ascii (Ojs.get_prop_ascii Ojs.global "Number") "NaN")
let negative_infinity : float =
Ojs.float_of_js
(Ojs.get_prop_ascii (Ojs.get_prop_ascii Ojs.global "Number")
"NEGATIVE_INFINITY")
let positive_infinity : float =
Ojs.float_of_js
(Ojs.get_prop_ascii (Ojs.get_prop_ascii Ojs.global "Number")
"POSITIVE_INFINITY")
end
module Static =
struct
type number = t
let rec number_of_js : Ojs.t -> number = fun (x27 : Ojs.t) -> t_of_js x27
and number_to_js : number -> Ojs.t = fun (x26 : t) -> t_to_js x26
type t = Ojs.t
let rec t_of_js : Ojs.t -> t = fun (x29 : Ojs.t) -> x29
and t_to_js : t -> Ojs.t = fun (x28 : Ojs.t) -> x28
let create : t -> 'any -> number =
fun (x31 : t) (x30 : 'any) ->
number_of_js (Ojs.new_obj (t_to_js x31) [|(Obj.magic x30)|])
let apply : t -> 'any -> float =
fun (x33 : t) (x32 : 'any) ->
Ojs.float_of_js (Ojs.apply (t_to_js x33) [|(Obj.magic x32)|])
let min_value : t -> float =
fun (x34 : t) ->
Ojs.float_of_js (Ojs.get_prop_ascii (t_to_js x34) "MIN_VALUE")
let max_value : t -> float =
fun (x35 : t) ->
Ojs.float_of_js (Ojs.get_prop_ascii (t_to_js x35) "MAX_VALUE")
let nan : t -> float =
fun (x36 : t) ->
Ojs.float_of_js (Ojs.get_prop_ascii (t_to_js x36) "NaN")
let negative_infinity : t -> float =
fun (x37 : t) ->
Ojs.float_of_js
(Ojs.get_prop_ascii (t_to_js x37) "NEGATIVE_INFINITY")
let positive_infinity : t -> float =
fun (x38 : t) ->
Ojs.float_of_js
(Ojs.get_prop_ascii (t_to_js x38) "POSITIVE_INFINITY")
end
let number : Static.t =
Static.t_of_js (Ojs.get_prop_ascii Ojs.global "Number")
================================================
FILE: node-test/bindings/expected/path.ml
================================================
[@@@js.dummy "!! This code has been generated by gen_js_api !!"]
[@@@ocaml.warning "-7-32-39"]
let sep : string = Ojs.string_of_js (Ojs.get_prop_ascii Imports.path "sep")
let dirname : string -> string =
fun (x1 : string) ->
Ojs.string_of_js
(Ojs.call Imports.path "dirname" [|(Ojs.string_to_js x1)|])
let extname : string -> string =
fun (x2 : string) ->
Ojs.string_of_js
(Ojs.call Imports.path "extname" [|(Ojs.string_to_js x2)|])
let is_absolute : string -> bool =
fun (x3 : string) ->
Ojs.bool_of_js
(Ojs.call Imports.path "isAbsolute" [|(Ojs.string_to_js x3)|])
let join : string list -> string =
fun (x4 : string list) ->
Ojs.string_of_js
(let x7 = Imports.path in
Ojs.call (Ojs.get_prop_ascii x7 "join") "apply"
[|x7;((let x5 =
Ojs.new_obj (Ojs.get_prop_ascii Ojs.global "Array") [||] in
List.iter
(fun (x6 : string) ->
ignore (Ojs.call x5 "push" [|(Ojs.string_to_js x6)|]))
x4;
x5))|])
let normalize : string -> string =
fun (x8 : string) ->
Ojs.string_of_js
(Ojs.call Imports.path "normalize" [|(Ojs.string_to_js x8)|])
type parse_result =
{
dir: string ;
root: string ;
base: string ;
name: string ;
ext: string }
let rec parse_result_of_js : Ojs.t -> parse_result =
fun (x10 : Ojs.t) ->
{
dir = (Ojs.string_of_js (Ojs.get_prop_ascii x10 "dir"));
root = (Ojs.string_of_js (Ojs.get_prop_ascii x10 "root"));
base = (Ojs.string_of_js (Ojs.get_prop_ascii x10 "base"));
name = (Ojs.string_of_js (Ojs.get_prop_ascii x10 "name"));
ext = (Ojs.string_of_js (Ojs.get_prop_ascii x10 "ext"))
}
and parse_result_to_js : parse_result -> Ojs.t =
fun (x9 : parse_result) ->
Ojs.obj
[|("dir", (Ojs.string_to_js x9.dir));("root",
(Ojs.string_to_js x9.root));
("base", (Ojs.string_to_js x9.base));("name",
(Ojs.string_to_js x9.name));
("ext", (Ojs.string_to_js x9.ext))|]
let parse : string -> parse_result =
fun (x11 : string) ->
parse_result_of_js
(Ojs.call Imports.path "parse" [|(Ojs.string_to_js x11)|])
================================================
FILE: node-test/bindings/expected/process.ml
================================================
[@@@js.dummy "!! This code has been generated by gen_js_api !!"]
[@@@ocaml.warning "-7-32-39"]
let env : string Container.StringMap.t =
Container.StringMap.t_of_js Ojs.string_of_js
(Ojs.get_prop_ascii (Ojs.get_prop_ascii Ojs.global "process") "env")
let version : string option =
Ojs.option_of_js Ojs.string_of_js
(Ojs.get_prop_ascii (Ojs.get_prop_ascii Ojs.global "process") "version")
================================================
FILE: node-test/bindings/expected/promise.ml
================================================
[@@@js.dummy "!! This code has been generated by gen_js_api !!"]
[@@@ocaml.warning "-7-32-39"]
module UntypedPromise =
struct
type t = Ojs.t
let rec t_of_js : Ojs.t -> t = fun (x2 : Ojs.t) -> x2
and t_to_js : t -> Ojs.t = fun (x1 : Ojs.t) -> x1
let resolve : Ojs.t -> Ojs.t =
fun (x3 : Ojs.t) ->
Ojs.call (Ojs.get_prop_ascii Ojs.global "Promise") "resolve" [|x3|]
let reject : Ojs.t -> Ojs.t =
fun (x4 : Ojs.t) ->
Ojs.call (Ojs.get_prop_ascii Ojs.global "Promise") "reject" [|x4|]
let then_ :
Ojs.t -> success:(Ojs.t -> Ojs.t) -> error:(Ojs.t -> Ojs.t) -> Ojs.t =
fun (x9 : Ojs.t) ~success:(x5 : Ojs.t -> Ojs.t)
~error:(x7 : Ojs.t -> Ojs.t) ->
Ojs.call x9 "then" [|(Ojs.fun_to_js 1 x5);(Ojs.fun_to_js 1 x7)|]
let all : Ojs.t list -> Ojs.t =
fun (x10 : Ojs.t list) ->
Ojs.call (Ojs.get_prop_ascii Ojs.global "Promise") "all"
[|(Ojs.list_to_js (fun (x11 : Ojs.t) -> x11) x10)|]
include
struct
type wrap = {
content: Ojs.t }
[@@@ocaml.warning "-7-32-39"]
let rec wrap_of_js : Ojs.t -> wrap =
fun (x13 : Ojs.t) ->
{ content = (Ojs.get_prop_ascii x13 "content") }
and wrap_to_js : wrap -> Ojs.t =
fun (x12 : wrap) -> Ojs.obj [|("content", (x12.content))|]
end
let is_promise o = (resolve o) == o
let wrap o = if is_promise o then wrap_to_js { content = o } else o
let unwrap o =
if Ojs.has_property o "content"
then Ojs.get_prop_ascii o "content"
else o
let return x = resolve (wrap x)
let fail err = reject (wrap err)
let bind ?(error= fail) p f =
then_ p ~success:(fun x -> f (unwrap x))
~error:(fun x -> error (unwrap x))
end
type 'a t = UntypedPromise.t
type error = Ojs.t
let fail error = UntypedPromise.fail error
let return x = UntypedPromise.return (Obj.magic x)
let bind ?error p f = UntypedPromise.bind ?error p (fun x -> f (Obj.magic x))
let prod p1 p2 =
bind (UntypedPromise.all [p1; p2])
(fun ojs ->
match Ojs.list_of_js Ojs.t_of_js ojs with
| x1::x2::[] -> return (x1, x2)
| _ -> assert false)
let map f p = bind p (fun x -> return (f x))
let t_to_js f p = UntypedPromise.t_to_js (map f p)
let t_of_js f p = map f (UntypedPromise.t_of_js p)
let (let+) p f = map f p
let (and+) = prod
let ( let* ) p f = bind p f
let ( and* ) = prod
let catch p error = bind p ~error return
================================================
FILE: node-test/bindings/fs.mli
================================================
[@@@js.scope "@node_fs.promises"]
module Dirent : sig
type t = Ojs.t
val t_of_js: Ojs.t -> t
val t_to_js: t -> Ojs.t
val name: t -> string [@@js.get]
val is_file: t -> bool [@@js.call]
val is_directory: t -> bool [@@js.call]
end
module Dir : sig
type t = Ojs.t
val t_of_js: Ojs.t -> t
val t_to_js: t -> Ojs.t
val path: t -> string [@@js.get]
val close: t -> unit Promise.t [@@js.call]
val read:t -> Dirent.t option Promise.t [@@js.call]
end
module FileHandle : sig
type t = Ojs.t
val t_of_js: Ojs.t -> t
val t_to_js: t -> Ojs.t
type read = {
bytes_read: int;
buffer: Buffer.t;
}
val append_file: t -> Buffer.t -> unit Promise.t [@@js.call]
val read: t -> Buffer.t -> int -> int -> int -> read Promise.t [@@js.call]
val chmod: t -> int -> unit Promise.t [@@js.call]
val chmown: t -> uid:int -> gid:int -> unit Promise.t [@@js.call]
val close: t -> unit Promise.t [@@js.call]
val datasync: t -> unit Promise.t [@@js.call]
val fd: t -> int [@@js.get]
end
val readdir: string -> string list Promise.t [@@js.global]
val open_: string -> flag:string -> FileHandle.t Promise.t [@@js.global]
val rmdir: string -> unit Promise.t [@@js.global]
val rename: string -> string -> unit Promise.t [@@js.global]
val unlink: string -> unit Promise.t [@@js.global]
================================================
FILE: node-test/bindings/global.mli
================================================
type timeout_id
val timeout_id_to_js: timeout_id -> Ojs.t
val timeout_id_of_js: Ojs.t -> timeout_id
type interval_id
val interval_id_to_js: interval_id -> Ojs.t
val interval_id_of_js: Ojs.t -> interval_id
val set_interval: (unit -> unit) -> int -> interval_id [@@js.global]
val set_timeout: (unit -> unit) -> int -> timeout_id [@@js.global]
val clear_timeout: timeout_id -> unit [@@js.global]
val clear_interval: interval_id -> unit [@@js.global]
================================================
FILE: node-test/bindings/imports.js
================================================
//Provides: node_path
var node_path = require('path');
//Provides: node_fs
var node_fs = require('fs');
================================================
FILE: node-test/bindings/imports.mli
================================================
val path: Ojs.t [@@js.global "@node_path"]
================================================
FILE: node-test/bindings/imports.wat
================================================
(global (export "_node_path") (import "js" "node_path") anyref)
(global (export "_node_fs") (import "js" "node_fs") anyref)
================================================
FILE: node-test/bindings/number.mli
================================================
type t = private Ojs.t
val toString: t -> ?radix:int -> unit -> float [@@js.call]
val toFixed: t -> ?fractionDigits:int -> unit -> float [@@js.call]
val toExponential: t -> ?fractionDigits:int -> unit -> float [@@js.call]
val toPrecision: t -> ?precision:int -> unit -> float [@@js.call]
val valueOf: t -> float [@@js.call]
(* scoped *)
module [@js.scope "Number"] Scoped : sig
val create: 'any -> t [@@js.create]
val invoke: 'any -> float [@@js.invoke]
val min_value: float [@@js.global "MIN_VALUE"]
val max_value: float [@@js.global "MAX_VALUE"]
val nan: float [@@js.global "NaN"]
val negative_infinity: float [@@js.global "NEGATIVE_INFINITY"]
val positive_infinity: float [@@js.global "POSITIVE_INFINITY"]
end
(* non-scoped *)
module Static : sig
type number = t
type t = private Ojs.t
val create: t -> 'any -> number [@@js.apply_newable]
val apply: t -> 'any -> float [@@js.apply]
val min_value: t -> float [@@js.get "MIN_VALUE"]
val max_value: t -> float [@@js.get "MAX_VALUE"]
val nan: t -> float [@@js.get "NaN"]
val negative_infinity: t -> float [@@js.get "NEGATIVE_INFINITY"]
val positive_infinity: t -> float [@@js.get "POSITIVE_INFINITY"]
end
val number: Static.t [@@js.global "Number"]
================================================
FILE: node-test/bindings/path.mli
================================================
[@@@js.scope Imports.path]
val sep: string [@@js.global]
val dirname: string -> string [@@js.global]
val extname: string -> string [@@js.global]
val is_absolute: string -> bool [@@js.global]
val join: (string list [@js.variadic]) -> string [@@js.global]
val normalize: string -> string [@@js.global]
type parse_result =
{
dir: string;
root: string;
base: string;
name: string;
ext: string
}
val parse: string -> parse_result [@@js.global]
================================================
FILE: node-test/bindings/process.mli
================================================
[@@@js.scope "process"]
val env : string Container.StringMap.t [@@js.global]
val version: string option [@@js.global]
================================================
FILE: node-test/bindings/promise.mli
================================================
module UntypedPromise : sig
type t = private Ojs.t
val t_to_js: t -> Ojs.t
val t_of_js: Ojs.t -> t
[@@@js.stop]
val return: Ojs.t -> t
val fail: Ojs.t -> t
val bind: ?error:(Ojs.t -> t) -> t -> (Ojs.t -> t) -> t
val all: Ojs.t list -> t
[@@@js.start]
[@@@js.implem
val resolve: Ojs.t -> Ojs.t [@@js.global "Promise.resolve"]
val reject: Ojs.t -> Ojs.t [@@js.global "Promise.reject"]
val then_: Ojs.t -> success:(Ojs.t -> Ojs.t) -> error:(Ojs.t -> Ojs.t) -> Ojs.t [@@js.call "then"]
val all: Ojs.t list -> Ojs.t [@@js.global "Promise.all"]
type wrap = { content: Ojs.t }[@@js]
let is_promise o =
resolve o == o
let wrap o =
if is_promise o then
wrap_to_js { content = o }
else o
let unwrap o =
if Ojs.has_property o "content" then
Ojs.get_prop_ascii o "content"
else
o
let return x = resolve (wrap x)
let fail err = reject (wrap err)
let bind ?(error = fail) p f =
then_ p ~success:(fun x -> f (unwrap x))
~error:(fun x -> error (unwrap x))
]
end
[@@@js.stop]
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
type error = Ojs.t
val fail: error -> 'a t
val return: 'a -> 'a t
val bind: ?error:(error -> 'b t) -> 'a t -> ('a -> 'b t) -> 'b t
val map: ('a -> 'b) -> 'a t -> 'b t
val prod: 'a t -> 'b t -> ('a * 'b) t
val ( let+ ): 'a t -> ('a -> 'b) -> 'b t
val ( and+ ): 'a t -> 'b t -> ('a * 'b) t
val ( let* ): 'a t -> ('a -> 'b t) -> 'b t
val ( and* ): 'a t -> 'b t -> ('a * 'b) t
val catch: 'a t -> (error -> 'a t) -> 'a t
[@@@js.start]
[@@@js.implem
type 'a t = UntypedPromise.t
type error = Ojs.t
let fail error = UntypedPromise.fail error
let return x = UntypedPromise.return (Obj.magic x)
let bind ?error p f =
UntypedPromise.bind ?error p
(fun x -> f (Obj.magic x))
let prod p1 p2 =
bind (UntypedPromise.all [p1; p2])
(fun ojs ->
match Ojs.list_of_js Ojs.t_of_js ojs with
| [x1; x2] -> return (x1, x2)
| _ -> assert false
)
let map f p = bind p (fun x -> return (f x))
let t_to_js f p = UntypedPromise.t_to_js (map f p)
let t_of_js f p = map f (UntypedPromise.t_of_js p)
let (let+) p f = map f p
let (and+) = prod
let (let*) p f = bind p f
let (and*) = prod
let catch p error = bind p ~error return
]
================================================
FILE: node-test/runtime_primitives/bindings.mli
================================================
module [@js.scope "@node_fs"] Fs : sig
val write_file_sync : string -> string -> unit [@@js.global "writeFileSync"]
val read_file_sync : string -> encoding:string -> string [@@js.global "readFileSync"]
val readdir_sync : string -> string array [@@js.global "readdirSync"]
val append_file_sync : string -> string -> unit [@@js.global "appendFileSync"]
end
module [@js.scope "@node_path"] Path : sig
val separator: string [@@js.global "sep"]
val join : (string list [@js.variadic]) -> string [@@js.global "join"]
end
val node_version : string [@@js.global "@node_version"]
val log : string -> unit [@@js.global "@node_console"]
================================================
FILE: node-test/runtime_primitives/dune
================================================
(rule
(targets bindings.ml)
(deps bindings.mli)
(action
(run gen_js_api %{deps})))
(executable
(name example)
(libraries ojs)
(preprocess
(pps gen_js_api.ppx))
(modes js wasm)
(js_of_ocaml
(javascript_files imports.js))
(wasm_of_ocaml
(javascript_files imports.js imports.wat)))
(rule
(alias runtest)
(enabled_if %{bin-available:node})
(action
(run node %{dep:./example.bc.js})))
(rule
(alias runtest-wasm)
(enabled_if %{bin-available:node})
(action
(run node %{dep:./example.bc.wasm.js})))
================================================
FILE: node-test/runtime_primitives/example.ml
================================================
open Bindings
let initial_content = "Hello, Node.js!"
let appended_line = "\nAppending a new line."
let encoding = "utf-8"
let filename = "example.txt"
let run () =
let file = Path.join ["."; filename] in
Fs.write_file_sync file initial_content;
let content = Fs.read_file_sync file ~encoding in
if content <> initial_content then
failwith "Unexpected initial content";
log ("File content: " ^ content);
let files = Fs.readdir_sync "." |> Array.to_list in
if not (List.mem filename files) then
failwith "example.txt missing from directory listing";
log ("Files in current directory: " ^ String.concat ", " files);
Fs.append_file_sync file appended_line;
let updated = Fs.read_file_sync file ~encoding in
if updated <> initial_content ^ appended_line then
failwith "Append failed";
log ("Updated content: " ^ updated);
log ("Path separator reported by Node: " ^ Path.separator);
log ("Node.js version: " ^ node_version)
let () = run ()
================================================
FILE: node-test/runtime_primitives/imports.js
================================================
'use strict';
//Provides: node_path
var node_path = require('path');
//Provides: node_fs
var node_fs = require('fs');
//Provides: node_version
var node_version = require('process').version;
//Provides: node_console
var node_console = console.log;
================================================
FILE: node-test/runtime_primitives/imports.wat
================================================
(global (export "_node_path") (import "js" "node_path") anyref)
(global (export "_node_fs") (import "js" "node_fs") anyref)
(global (export "_node_version") (import "js" "node_version") anyref)
(global (export "_node_console") (import "js" "node_console") anyref)
================================================
FILE: node-test/test1/dune
================================================
(executable
(name test)
(libraries ojs node)
(preprocess
(pps gen_js_api.ppx))
(modes js wasm)
(js_of_ocaml
(javascript_files recursive.js))
(wasm_of_ocaml
(javascript_files recursive.js)))
(rule
(targets recursive.ml)
(deps recursive.mli)
(action
(run gen_js_api %{deps})))
(rule
(alias runtest)
(enabled_if %{bin-available:node})
(action
(run node %{dep:./test.bc.js})))
(rule
(alias runtest-wasm)
(enabled_if %{bin-available:node})
(action
(run node %{dep:./test.bc.wasm.js})))
================================================
FILE: node-test/test1/recursive.js
================================================
var Foo = /*#__PURE__*/function () {
"use strict";
function Foo(name) {
this.name = name;
}
var _proto = Foo.prototype;
_proto.describe = function describe() {
return "Foo:".concat(this.name);
};
_proto.toBar = function toBar() {
return new Bar(this.name);
};
return Foo;
}();
var Bar = /*#__PURE__*/function () {
"use strict";
function Bar(name) {
this.name = name;
}
var _proto2 = Bar.prototype;
_proto2.describe = function describe() {
return "Bar:".concat(this.name);
};
_proto2.toFoo = function toFoo() {
return new Foo(this.name);
};
return Bar;
}();
globalThis.Foo = Foo
globalThis.Bar = Bar
================================================
FILE: node-test/test1/recursive.mli
================================================
module [@js.scope "Foo"] rec Foo : sig
type t = private Ojs.t
val t_of_js: Ojs.t -> t
val t_to_js: t -> Ojs.t
val create: string -> t [@@js.create]
val describe: t -> string [@@js.call "describe"]
val to_bar: t -> Bar.t [@@js.call "toBar"]
end
and [@js.scope "Bar"] Bar : sig
type t = private Ojs.t
val t_of_js: Ojs.t -> t
val t_to_js: t -> Ojs.t
val create: string -> t [@@js.create]
val describe: t -> string [@@js.call "describe"]
val to_foo: t -> Foo.t [@@js.call "toFoo"]
end
================================================
FILE: node-test/test1/test.ml
================================================
(* The gen_js_api is released under the terms of an MIT-like license. *)
(* See the attached LICENSE file. *)
(* Copyright 2015 by LexiFi. *)
[@@@ocaml.warning "-32-34"]
open Node
let check_node_version version =
let major_version = function
| Some s when String.length s > 0 && s.[0] = 'v' ->
begin match
String.sub s 1 (String.length s - 1)
|> String.split_on_char '.'
with
| [] -> None
| hd :: _ -> int_of_string_opt hd
end
| _ -> None
in
if Option.value ~default:(-1) (major_version Process.version) < version then
begin
Printf.eprintf "[WARNING] Ignoring test: it requires Node > %d; please upgrade (current version %s)" version
(Option.value ~default:"???" Process.version);
exit 0
end
let () =
check_node_version 18
(** Buffer **)
let caml_from_set s =
let len = String.length s in
let buf = Buffer.alloc len in
String.iteri (fun k x ->
Buffer.set buf k (Char.code x)
) s;
buf
let caml_from_write s =
let len = String.length s in
let buf = Buffer.alloc len in
let written = Buffer.write buf s in
assert (written = len);
buf
let assert_equal_buffer b1 b2 =
let len1 = Buffer.length b1 in
let len2 = Buffer.length b2 in
assert (len1 = len2);
for k = 0 to len1 -1 do
assert (Buffer.get b1 k = Buffer.get b2 k)
done
let copy src =
let len = Buffer.length src in
let dst = Buffer.alloc len in
let written =
Buffer.copy src ~dst ~start:0 ~dst_start:0 ~dst_end:len
in
assert (len = written);
dst
let () =
let test = "test" in
let native = Buffer.from test in
let from_set = caml_from_set test in
let from_write = caml_from_write test in
let from_copy = copy native in
assert_equal_buffer native from_set;
assert_equal_buffer native from_write;
assert_equal_buffer native from_copy
(** Process **)
let () =
Container.StringMap.iter
(fun key value ->
assert (Sys.getenv key = value)
)
Process.env
let uncaught_handler p =
let open Promise in
catch p (fun error ->
Printf.eprintf "Uncaught exception: %s\n" (Printexc.to_string (Obj.magic error));
exit 1
)
(** FileSystem **)
let root : unit Promise.t =
let open Promise in
uncaught_handler
begin
let* contents = Fs.readdir "."
and+ contents' = Fs.readdir "." in
List.iter2 (fun x y ->
assert (x = y)) contents contents';
return ()
end
(*** Index signature **)
include [%js:
module ArrayLike (K : Ojs.T) : sig
type t = private Ojs.t
val t_to_js: t -> Ojs.t
val t_of_js: Ojs.t -> t
val create: unit -> t [@@js.builder]
val get: t -> int -> K.t option [@@js.index_get]
val set: t -> int -> K.t -> unit [@@js.index_set]
end
module MapLike (K : Ojs.T) : sig
type t = private Ojs.t
val t_to_js: t -> Ojs.t
val t_of_js: Ojs.t -> t
val create: unit -> t [@@js.builder]
val get: t -> string -> K.t option [@@js.index_get]
val set: t -> string -> K.t -> unit [@@js.index_set]
end
module Symbol : sig
type t = private Ojs.t
val t_to_js: t -> Ojs.t
val t_of_js: Ojs.t -> t
val fresh: unit -> t [@@js.global "Symbol"]
end
module SymbolMap (K : Ojs.T) : sig
type t = private Ojs.t
val t_to_js: t -> Ojs.t
val t_of_js: Ojs.t -> t
val create: unit -> t [@@js.builder]
val get: t -> Symbol.t -> K.t option [@@js.index_get]
val set: t -> Symbol.t -> K.t -> unit [@@js.index_set]
end
]
let () =
let module M = MapLike([%js: type t = string val t_to_js: t -> Ojs.t val t_of_js: Ojs.t -> t]) in
let map_str = M.create () in
M.set map_str "foo" "bar";
assert (M.get map_str "foo" = Some "bar");
M.set map_str "baz" "boo";
assert (M.get map_str "baz" = Some "boo");
let module A = ArrayLike([%js: type t = int val t_to_js: t -> Ojs.t val t_of_js: Ojs.t -> t]) in
let map_int = A.create () in
let len = 10 in
for k = 0 to len - 1 do
A.set map_int k k;
assert (A.get map_int k = Some k);
A.set map_int k (k * k);
assert (A.get map_int k = Some (k * k));
done;
let module M = SymbolMap([%js: type t = string val t_to_js: t -> Ojs.t val t_of_js: Ojs.t -> t]) in
let a = Symbol.fresh () in
let b = Symbol.fresh () in
let map_str = M.create () in
M.set map_str a "bar";
assert (M.get map_str a = Some "bar");
M.set map_str b "boo";
assert (M.get map_str b = Some "boo")
(*** Function signature **)
include [%js:
module Concat : sig
type t = private Ojs.t
val t_to_js: t -> Ojs.t
val t_of_js: Ojs.t -> t
val apply: t -> (string list [@js.variadic]) -> string [@@js.apply]
end
module [@js.scope Imports.path] Path2 : sig
val join: Concat.t [@@js.global "join"]
end
]
let () =
let args = ["foo"; "bar"; "baz"] in
let res1 = Path.join args in
let res2 = Concat.apply Path2.join args in
assert (res1 = res2);
()
(*** Newable function *)
include [%js:
module Date: sig
type t = private Ojs.t
val getUTCFullYear: t -> float [@@js.call "getUTCFullYear"]
val getUTCMonth: t -> float [@@js.call "getUTCMonth"]
val getUTCDate: t -> float [@@js.call "getUTCDate"]
end
module DateConstructor: sig
type t = private Ojs.t
val utc:
t ->
year:int -> month:int -> ?date:int ->
?hours:int -> ?minutes:int -> ?seconds:int ->
?ms:int -> unit -> float [@@js.call "UTC"]
val new_:
t -> float -> Date.t [@@js.apply_newable]
end
val date: DateConstructor.t [@@js.global "Date"]
]
let () =
let d =
DateConstructor.new_ date
(DateConstructor.utc date ~year:1999 ~month:11 ~date:31 ())
in
assert (int_of_float (Date.getUTCFullYear d) == 1999);
assert (int_of_float (Date.getUTCMonth d) == 11);
assert (int_of_float (Date.getUTCDate d) == 31);
()
(** Arrays **)
let () =
let open Arrays.StringArray in
let a = create () in
for k = 0 to 10 do
push a (string_of_int k);
done;
let s = join a "," in
List.iteri (fun k x ->
assert (string_of_int k = x)
) (String.split_on_char ',' s)
(** Invoking a global object **)
(** https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Number/Number **)
let () =
let check (a: Number.t) (b: float) =
assert (Ojs.instance_of (a :> Ojs.t) ~constr:(Number.number :> Ojs.t));
assert (not (Ojs.instance_of (Ojs.float_to_js b) ~constr:(Number.number :> Ojs.t)));
assert (Number.valueOf a = b);
()
in
let s = Ojs.string_to_js "123" in
check (Number.Scoped.create s) (Number.Scoped.invoke s);
check (Number.Static.create Number.number s) (Number.Static.apply Number.number s);
assert (Number.Scoped.max_value = Number.Static.max_value Number.number);
()
(** Using recursive modules **)
let () =
let open Recursive in
let fooA = Foo.create "A" in
assert (Foo.describe fooA = "Foo:A");
let barA = Foo.to_bar fooA in
assert (Bar.describe barA = "Bar:A");
let fooA' = Bar.to_foo barA in
assert (Foo.describe fooA' = "Foo:A");
()
(** Using first class modules **)
include [%js:
val to_string: (module[@js] Ojs.T with type t = 'a) -> 'a -> string [@@js.call "toString"]
]
let () =
let check (type a) (module A : Ojs.T with type t = a) (value: a) (expected: string) =
let str = to_string (module A) value in
assert (str = expected)
in
check (module Ojs.Int) 42 "42";
check (module Ojs.Float) 4.2 "4.2";
check (module Ojs.String) "hello" "hello";
check (module Ojs.List(Ojs.Int)) [4;2] "4,2";
check (module Ojs.List(Ojs.String)) ["hello"; "world"] "hello,world";
()
let () =
let open Arrays.JsArray2 in
let a = create () in
for k = 0 to 10 do
push (module Ojs.String) a (string_of_int k);
done;
let sa = join a "," in
List.iteri (fun k x ->
assert (string_of_int k = x)
) (String.split_on_char ',' sa);
let b =
let orig = List.init 11 string_of_int in
create' (module Ojs.String) orig
in
let sb = join b "," in
assert (sa = sb);
assert (get (module Ojs.String) a 0 = Some "0");
set (module Ojs.String) a 1 "foo";
assert (get (module Ojs.String) a 1 = Some "foo");
()
================================================
FILE: ojs.opam
================================================
# This file is generated by dune, edit dune-project instead
opam-version: "2.0"
version: "1.1.7"
synopsis: "Runtime Library for gen_js_api generated libraries"
description: "To be used in conjunction with gen_js_api"
maintainer: ["Alain Frisch <alain.frisch@lexifi.com>"]
authors: [
"Alain Frisch <alain.frisch@lexifi.com>"
"Sebastien Briais <sebastien.briais@lexifi.com>"
]
license: "MIT"
homepage: "https://github.com/LexiFi/gen_js_api"
bug-reports: "https://github.com/LexiFi/gen_js_api/issues"
depends: [
"dune" {>= "3.17"}
"ocaml" {>= "4.13"}
"js_of_ocaml-compiler" {>= "6.3.0"}
"odoc" {with-doc}
]
dev-repo: "git+https://github.com/LexiFi/gen_js_api.git"
build: [
["dune" "subst"] {dev}
["dune" "build" "-p" name "-j" jobs "@install" "@doc" {with-doc}]
]
================================================
FILE: ojs.opam.template
================================================
build: [
["dune" "subst"] {dev}
["dune" "build" "-p" name "-j" jobs "@install" "@doc" {with-doc}]
]
================================================
FILE: ppx-driver/dune
================================================
(library
(name gen_js_api_ppx_driver)
(public_name gen_js_api.ppx)
(synopsis "Syntactic support for gen_js_api")
(libraries gen_js_api.lib ppxlib.ast ppxlib)
(kind ppx_rewriter)
(ppx_runtime_libraries ojs)
(preprocess no_preprocessing))
================================================
FILE: ppx-driver/gen_js_api_ppx_driver.ml
================================================
let check_attributes_with_ppxlib = false
let check_locations_with_ppxlib = false
let () =
if check_attributes_with_ppxlib
then (
Ppxlib.Driver.enable_checks ();
Gen_js_api_ppx.check_attribute := false
);
if check_locations_with_ppxlib
then (
Ppxlib.Driver.enable_location_check ()
);
let mapper_for_sig =
Gen_js_api_ppx.mark_attributes_as_used
in
let mapper_for_str =
Gen_js_api_ppx.mark_attributes_as_used
in
let module_expr_ext =
let rewriter ~loc ~path:_ si =
Gen_js_api_ppx.module_expr_rewriter ~loc ~attrs:[] si
in
Ppxlib.Extension.declare "js"
Ppxlib.Extension.Context.Module_expr
Ppxlib.(Ast_pattern.psig Ast_pattern.__)
rewriter
|> Ppxlib.Context_free.Rule.extension
in
let ext_to =
let rewriter ~loc ~path:_ core_type =
Gen_js_api_ppx.js_to_rewriter ~loc core_type
in
Ppxlib.Extension.declare "js.to"
Ppxlib.Extension.Context.Expression
Ppxlib.(Ast_pattern.ptyp Ast_pattern.__)
rewriter
|> Ppxlib.Context_free.Rule.extension
in
let ext_of =
let rewriter ~loc ~path:_ core_type =
Gen_js_api_ppx.js_of_rewriter ~loc core_type
in
Ppxlib.Extension.declare "js.of"
Ppxlib.Extension.Context.Expression
Ppxlib.(Ast_pattern.ptyp Ast_pattern.__)
rewriter
|> Ppxlib.Context_free.Rule.extension
in
let attr_typ =
let rewriter ~ctxt (rec_flag : Ppxlib.Asttypes.rec_flag) tdl _ =
Gen_js_api_ppx.type_decl_rewriter
~loc:(Ppxlib.Expansion_context.Deriver.derived_item_loc ctxt)
rec_flag tdl
in
Ppxlib.Context_free.Rule.attr_str_type_decl
(Ppxlib.Attribute.declare "js"
Ppxlib.Attribute.Context.type_declaration
Ppxlib.(Ast_pattern.pstr Ast_pattern.nil) ())
rewriter
in
Ppxlib.Driver.register_transformation
"gen_js_api"
~rules:[module_expr_ext; ext_of; ext_to; attr_typ ]
~impl:(mapper_for_str # structure)
~intf:(mapper_for_sig # signature)
================================================
FILE: ppx-lib/dune
================================================
(library
(name gen_js_api_ppx)
(public_name gen_js_api.lib)
(libraries compiler-libs.common ppxlib)
(ppx_runtime_libraries ojs)
(preprocess no_preprocessing))
================================================
FILE: ppx-lib/gen_js_api_ppx.ml
================================================
(* The gen_js_api is released under the terms of an MIT-like license. *)
(* See the attached LICENSE file. *)
(* Copyright 2015 by LexiFi. *)
open Ppxlib
open Asttypes
open Parsetree
open Longident
open Ast_helper
open Location
let mkloc txt loc =
{ txt; loc }
let mknoloc txt =
mkloc txt !Ast_helper.default_loc
(** Errors *)
type error =
| Expression_expected
| Identifier_expected
| Structure_expected
| Invalid_expression
| Multiple_binding_declarations
| Binding_type_mismatch
| Cannot_parse_type
| Cannot_parse_sigitem
| Cannot_parse_classdecl
| Cannot_parse_classfield
| Implicit_name of string
| Not_supported_here of string
| Record_expected of string
| Record_constructor_in_union
| Unknown_union_method
| Non_constant_constructor_in_enum
| Multiple_default_case
| Duplicate_case_value of location * location
| Invalid_variadic_type_arg
| No_input
| Multiple_inputs
| Unlabelled_argument_in_builder
| Spurious_attribute of label
| Sum_kind_args
| Union_without_discriminator
| Contravariant_type_parameter of string
| Cannot_set_runtime_value of string
exception Error of Location.t * error
let is_ascii s =
let exception Break in
try
String.iter (fun c -> if Char.code c > 127 then raise Break) s;
true
with Break -> false
let check_attribute = ref true
let used_attributes_tbl = Hashtbl.create 16
(* [merlin_hide] tells merlin to not look at a node, or at any of its
descendants. *)
let merlin_hide =
{ attr_name = { txt = "merlin.hide"; loc = Location.none }
; attr_payload = PStr []
; attr_loc = Location.none
}
let register_loc attr =
Ppxlib.Attribute.mark_as_handled_manually attr;
Hashtbl.replace used_attributes_tbl attr.attr_name.loc ()
let is_registered_loc loc = Hashtbl.mem used_attributes_tbl loc
let error loc err = raise (Error (loc, err))
let filter_attr_name key attr =
if attr.attr_name.txt = key then begin
register_loc attr;
true
end else false
let filter_extension key name = name.txt = key
let has_attribute key attrs = List.exists (filter_attr_name key) attrs
let get_attribute key attrs =
match List.find (filter_attr_name key) attrs with
| exception Not_found -> None
| attr -> Some attr
let unoption = function
| Some x -> x
| None -> assert false
let expr_of_stritem = function
| {pstr_desc=Pstr_eval (e, _); _} -> e
| p -> error p.pstr_loc Expression_expected
let expr_of_payload {attr_loc; attr_payload; _} =
match attr_payload with
| PStr [x] -> expr_of_stritem x
| _ -> error attr_loc Expression_expected
let str_of_payload {attr_loc; attr_payload; _} =
match attr_payload with
| PStr x -> x
| _ -> error attr_loc Structure_expected
let id_of_expr = function
| {pexp_desc=Pexp_constant (Pconst_string (s, _, _)); _} -> s
| e -> error e.pexp_loc Identifier_expected
let get_expr_attribute key attrs =
match get_attribute key attrs with
| None -> None
| Some payload -> Some (expr_of_payload payload)
let get_string_attribute key attrs =
match get_attribute key attrs with
| None -> None
| Some payload -> Some (id_of_expr (expr_of_payload payload))
let get_string_attribute_default key default attrs =
match get_attribute key attrs with
| None -> default
| Some payload -> payload.attr_loc, id_of_expr (expr_of_payload payload)
let print_error ppf = function
| Expression_expected ->
Format.fprintf ppf "Expression expected"
| Structure_expected ->
Format.fprintf ppf "Structure expected"
| Identifier_expected ->
Format.fprintf ppf "String literal expected"
| Invalid_expression ->
Format.fprintf ppf "Invalid expression"
| Multiple_binding_declarations ->
Format.fprintf ppf "Multiple binding declarations"
| Binding_type_mismatch ->
Format.fprintf ppf "Binding declaration and type are not compatible"
| Cannot_parse_type ->
Format.fprintf ppf "Cannot parse type"
| Cannot_parse_sigitem ->
Format.fprintf ppf "Cannot parse signature item"
| Cannot_parse_classdecl ->
Format.fprintf ppf "Cannot parse class declaration"
| Cannot_parse_classfield ->
Format.fprintf ppf "Cannot parse class field"
| Implicit_name prefix ->
Format.fprintf ppf "Implicit name must start with '%s' and cannot be empty" prefix
| Not_supported_here msg ->
Format.fprintf ppf "%s not supported in this context" msg
| Non_constant_constructor_in_enum ->
Format.fprintf ppf "Constructors in enums cannot take arguments"
| Multiple_default_case ->
Format.fprintf ppf "At most one default constructor is supported in variants"
| Duplicate_case_value (loc1, loc2) ->
let line1, line2 = loc1.loc_start.pos_lnum, loc2.loc_start.pos_lnum in
let line1, line2 = if line1 < line2 then line1, line2 else line2, line1 in
Format.fprintf ppf "This case value is used twice at lines %d and %d" line1 line2
| Invalid_variadic_type_arg ->
Format.fprintf ppf "A variadic function argument must be of type list"
| No_input ->
Format.fprintf ppf "An input file must be provided"
| Multiple_inputs ->
Format.fprintf ppf "A single input file must be provided"
| Unlabelled_argument_in_builder ->
Format.fprintf ppf "Arguments of builder must be named"
| Spurious_attribute label ->
Format.fprintf ppf "Spurious %s attribute" label
| Sum_kind_args ->
Format.fprintf ppf "Incompatible label name for 'kind' and constructor arguments."
| Record_constructor_in_union ->
Format.fprintf ppf "Constructors in unions must not be an inline record."
| Unknown_union_method ->
Format.fprintf ppf "Unknown method to discriminate unions."
| Union_without_discriminator ->
Format.fprintf ppf "js.union without way to discriminate values."
| Contravariant_type_parameter label ->
Format.fprintf ppf "Contravariant type parameter '%s is not allowed." label
| Record_expected shape ->
Format.fprintf ppf "Record %s expected." shape
| Cannot_set_runtime_value name ->
Format.fprintf ppf "Cannot set runtime value '%s'." name
let () =
Location.Error.register_error_of_exn
(function
| Error (loc, err) ->
let createf ~loc fmt =
Format.kasprintf
(fun str -> Location.Error.make ~loc ~sub:[] str) fmt
in
Some (createf ~loc "%a" print_error err)
| _ -> None
)
(*
let show_attrs attrs =
prerr_endline "===========";
prerr_endline "attributes:";
List.iter (fun ({txt; loc = _}, _) -> prerr_endline txt) attrs
*)
let js_name ~global_attrs ?(capitalize = false) name =
if has_attribute "js.verbatim_names" global_attrs then
if capitalize then String.capitalize_ascii name
else name
else
let n = String.length name in
let buf = Buffer.create n in
let capitalize = ref capitalize in
for i = 0 to n-1 do
let c = name.[i] in
if c = '_' then capitalize := true
else if !capitalize then begin
Buffer.add_char buf (Char.uppercase_ascii c);
capitalize := false
end else Buffer.add_char buf c
done;
Buffer.contents buf
let get_js_constr ~global_attrs name attributes =
match get_attribute "js" attributes with
| None -> `String (js_name ~global_attrs name)
| Some payload ->
begin match (expr_of_payload payload).pexp_desc with
| Pexp_constant (Pconst_string (s, _, _)) -> `String s
| Pexp_constant (Pconst_integer (n, _)) -> `Int n
| Pexp_constant (Pconst_float (f, _)) -> `Float f
| Pexp_construct (ident_loc, _) ->
begin match ident_loc.txt with
| Lident "true" -> `Bool true
| Lident "false" -> `Bool false
| _ -> error ident_loc.loc Invalid_expression
end
| _ -> error payload.attr_loc Invalid_expression
end
(** AST *)
type typ =
| Arrow of arrow_params
| Unit of Location.t
| Js
| Name of string * typ list
| Variant of { location: Location.t;
global_attrs:attributes;
attributes:attributes;
constrs:constructor list }
| Tuple of typ list
| Typ_var of string
| Packaged_type of { local_name: string; (* `a` specified by `(type a)`*)
module_name: string (* `A` as in `(module A : Ojs.T with type t = a)` *) }
and lab =
| Arg
| Lab of {ml: string}
| Opt of {ml: string; def: Parsetree.expression option}
and arg =
{
lab: lab;
att: attributes;
typ: typ;
}
and arrow_params =
{
ty_args: arg list;
ty_vararg: arg option;
unit_arg: bool;
ty_res: typ;
}
and constructor_arg =
| Constant
| Unary of typ
| Nary of typ list
| Record of (Location.t * lid * string * typ) list
and constructor =
{
mlconstr: string;
arg: constructor_arg;
attributes: attributes;
location: Location.t;
}
let arg_label = function
| Arg -> Nolabel
| Lab {ml; _} -> Labelled ml
| Opt {ml; _} -> Optional ml
type apply_type =
| Function (* f(..) *)
| NewableFunction (* new f(..) *)
type valdef =
| Cast
| Ignore
| PropGet of string
| PropSet of string
| IndexGet
| IndexSet
| MethCall of string
| Apply of apply_type
| Invoke
| Global of string
| New of string option
| Builder of attributes
| Auto of valdef
let rec string_of_valdef = function
| Cast -> "js.cast"
| Ignore -> "js.ignore"
| PropGet _ -> "js.get"
| PropSet _ -> "js.set"
| IndexGet -> "js.index_get"
| IndexSet -> "js.index_set"
| MethCall _ -> "js.call"
| Apply Function -> "js.apply"
| Apply NewableFunction -> "js.apply_newable"
| Invoke -> "js.invoke"
| Global _ -> "js.global"
| New None -> "js.create"
| New (Some _) -> "js.new"
| Builder _ -> "js.builder"
| Auto valdef -> string_of_valdef valdef
let auto_deprecation_attribute loc valdef =
let message =
Printf.sprintf
"Heuristic for automatic binding is deprecated; please add the '@%s' attribute."
(string_of_valdef valdef)
in
attribute_of_warning loc message
type methoddef =
| Getter of string
| Setter of string
| IndexGetter
| IndexSetter
| MethodCall of string
| ApplyAsFunction of apply_type
type method_decl =
{
method_name: string;
method_typ: typ;
method_def: methoddef;
method_loc: Location.t;
method_attrs: attributes
}
type class_field =
| Method of method_decl
| Inherit of Longident.t Location.loc
type classdecl =
| Declaration of { class_name: string; class_fields: class_field list }
| Constructor of { class_name: string; js_class_name: string; class_arrow: arrow_params }
type decl =
| Module of functor_parameter list * string * decl list
| RecModule of (module_type * functor_parameter list * string * decl list) list
| ModuleAlias of string * Longident.t Location.loc
| Type of rec_flag * Parsetree.type_declaration list * attributes
| Val of { name:string;
ty: typ;
decl: valdef;
loc: Location.t;
packages: (string * string) list; (** reversed order, (local_name, module_name) *)
global_attrs: attributes }
| Class of classdecl list
| Implem of Parsetree.structure
| Open of Parsetree.open_description
| Include of Parsetree.module_expr Parsetree.include_infos
(** Parsing *)
let local_type_of_type_var label =
"__"^label
let neg_variance = function
| -1 -> 1
| 0 | 1 -> -1
| _ -> invalid_arg "neg_variance"
let no_attributes attributes =
List.iter (fun attr ->
ignore (filter_attr_name "js.dummy" attr)
) attributes;
attributes = []
type type_context =
{
type_params: string list;
packages: (string * string) list; (** reversed order, (local_name, module_name) *)
}
let empty_type_context = { type_params = []; packages = [] }
let rec parse_arg ~variance (ctx: type_context) lab ~global_attrs ty =
let lab =
match lab with
| Nolabel -> Arg
| Labelled ml -> Lab {ml}
| Optional ml ->
Opt {ml; def=get_expr_attribute "js.default" ty.ptyp_attributes}
in
{
lab;
att=ty.ptyp_attributes;
typ = parse_typ ~variance:(neg_variance variance) ctx ~global_attrs ty;
}
and parse_typ ~variance ctx ~global_attrs ty =
match ty.ptyp_desc with
| Ptyp_arrow (lab, t1, t2) when has_attribute "js.variadic" t1.ptyp_attributes ->
begin match parse_arg ~variance ctx lab ~global_attrs t1 with
| {lab; att; typ=Name ("list", [typ])} ->
let ty_vararg = Some {lab; att; typ} in
begin match parse_typ ~variance ctx ~global_attrs t2 with
| Arrow ({ty_args = []; ty_vararg = None; unit_arg = _; ty_res = _} as params) when no_attributes t2.ptyp_attributes ->
Arrow {params with ty_vararg}
| Arrow _ when t2.ptyp_attributes = [] -> error ty.ptyp_loc Cannot_parse_type
| tres -> Arrow {ty_args = []; ty_vararg; unit_arg = false; ty_res = tres}
end
| _ -> error t1.ptyp_loc Invalid_variadic_type_arg
end
| Ptyp_arrow (lab, t1, t2) ->
let t1 = parse_arg ~variance ctx lab ~global_attrs t1 in
begin match parse_typ ~variance ctx ~global_attrs t2 with
| Arrow ({ty_args; ty_vararg = _; unit_arg = _; ty_res = _} as params) when no_attributes t2.ptyp_attributes -> Arrow {params with ty_args = t1 :: ty_args}
| tres ->
begin match t1 with
| {lab=Arg; att=[]; typ=Unit _} -> Arrow {ty_args = []; ty_vararg = None; unit_arg = true; ty_res = tres}
| _ -> Arrow {ty_args = [t1]; ty_vararg = None; unit_arg = false; ty_res = tres}
end
end
| Ptyp_constr ({txt = lid; loc = _}, tl) ->
begin match String.concat "." (Longident.flatten_exn lid), tl with
| "unit", [] -> Unit ty.ptyp_loc
| "Ojs.t", [] -> Js
| s, tl -> Name (s, List.map (parse_typ ~variance ctx ~global_attrs) tl)
end
| Ptyp_variant (rows, Closed, None) ->
let location = ty.ptyp_loc in
let prepare_row = function
| {prf_desc = Rtag ({txt = mlconstr; _}, true, []); prf_attributes = attributes; prf_loc = location} ->
{ mlconstr; arg = Constant; attributes; location }
| {prf_desc = Rtag ({txt = mlconstr; _}, false, [typ]); prf_attributes = attributes; prf_loc = location} ->
begin match parse_typ ~variance ctx ~global_attrs typ with
| Tuple typs -> { mlconstr; arg = Nary typs; attributes; location }
| typ -> { mlconstr; arg = Unary typ; attributes; location }
end
| _ -> error location Cannot_parse_type
in
Variant {location; global_attrs; attributes = ty.ptyp_attributes; constrs = List.map prepare_row rows}
| Ptyp_tuple typs ->
let typs = List.map (parse_typ ~variance ctx ~global_attrs) typs in
Tuple typs
| Ptyp_var label ->
if List.mem label ctx.type_params then
if variance < 0 then
error ty.ptyp_loc (Contravariant_type_parameter label)
else
Name (local_type_of_type_var label, [])
else
begin match List.assoc_opt label ctx.packages with
| Some module_name -> Packaged_type { local_name = label; module_name }
| None -> Typ_var label
end
| _ ->
error ty.ptyp_loc Cannot_parse_type
let parse_typ = parse_typ ~variance:0
let check_prefix ~prefix s =
let l = String.length prefix in
if l <= String.length s && String.sub s 0 l = prefix
then
Some (String.sub s l (String.length s - l))
else
None
let has_prefix ~prefix s = check_prefix ~prefix s <> None
let drop_prefix ~prefix s =
match check_prefix ~prefix s with
| Some x -> x
| None -> assert false
let check_suffix ~suffix s =
let l = String.length suffix in
if l <= String.length s && String.sub s (String.length s - l) l = suffix
then
Some (String.sub s 0 (String.length s - l))
else
None
let rec choose f = function
| [] -> []
| x :: xs ->
begin match f x with
| None -> choose f xs
| Some y -> y :: choose f xs
end
let derived_from_type s ty =
match ty with
| Arrow {ty_args; ty_vararg = None; unit_arg = false; ty_res = Js} ->
begin match List.rev ty_args with
| {lab=Arg; att=_; typ=Name (t, _);} :: _ -> check_suffix ~suffix:"_to_js" s = Some t
| _ -> false
end
| Arrow {ty_res = Name (t, _); ty_vararg = None; unit_arg = false; ty_args } ->
begin match List.rev ty_args with
| {lab=Arg; att=_; typ= Js;} :: _ -> check_suffix ~suffix:"_of_js" s = Some t
| _ -> false
end
| _ -> false
let auto ~global_attrs s ty =
if derived_from_type s ty then
Ignore
else
Auto begin match ty with
| Arrow {ty_args = _; ty_vararg = None; unit_arg = _; ty_res = Name _} when s = "create" -> New None
| Arrow {ty_args = _; ty_vararg = None; unit_arg = _; ty_res = Name _} when has_prefix ~prefix:"new_" s -> New (Some (js_name ~capitalize:true ~global_attrs (drop_prefix ~prefix:"new_" s)))
| Arrow {ty_args = [_]; ty_vararg = None; unit_arg = false; ty_res = Unit _} when has_prefix ~prefix:"set_" s -> PropSet (js_name ~global_attrs (drop_prefix ~prefix:"set_" s))
| Arrow {ty_args = [{lab=Arg; att=_; typ=Name _}; _; _]; ty_vararg = None; unit_arg = false; ty_res = Unit _} when s = "set" -> IndexSet
| Arrow {ty_args = [{lab=Arg; att=_; typ=Name _}; _]; ty_vararg = None; unit_arg = false; ty_res = Unit _} when has_prefix ~prefix:"set_" s -> PropSet (js_name ~global_attrs (drop_prefix ~prefix:"set_" s))
| Arrow {ty_args = [{lab=Arg; att=_; typ=Name _}]; ty_vararg = None; unit_arg = false; ty_res = Unit _} -> MethCall (js_name ~global_attrs s)
| Arrow {ty_args = [{lab=Arg; att=_; typ=Name _}; _]; ty_vararg = None; unit_arg = false; ty_res = _} when s = "get" -> IndexGet
| Arrow {ty_args = [{lab=Arg; att=_; typ=Name _}]; ty_vararg = None; unit_arg = false; ty_res = _} -> PropGet (js_name ~global_attrs s)
| Arrow {ty_args = []; ty_vararg = None; unit_arg = true; ty_res = _} -> PropGet (js_name ~global_attrs s)
| Arrow {ty_args = {lab=Arg; att=_; typ=Name _} :: _; ty_vararg = _; unit_arg = _; ty_res = _} when s = "apply" -> Apply Function
| Arrow {ty_args = {lab=Arg; att=_; typ=Name _} :: _; ty_vararg = _; unit_arg = _; ty_res = _} -> MethCall (js_name ~global_attrs s)
| _ -> Global (js_name ~global_attrs s)
end
let auto_in_object ~global_attrs s typ =
Auto begin match typ with
| Arrow {ty_args = [{lab=Arg; att=_; typ=_}]; ty_vararg = None; unit_arg = false; ty_res = Unit _} when has_prefix ~prefix:"set_" s -> PropSet (js_name ~global_attrs (drop_prefix ~prefix:"set_" s))
| Arrow {ty_args = [_]; ty_vararg = None; unit_arg = _; ty_res = _} when s = "get" -> IndexGet
| Arrow {ty_args = [_; _]; ty_vararg = None; unit_arg = false; ty_res = Unit _} when s = "set" -> IndexSet
| Arrow _ when s = "apply" -> Apply Function
| Arrow _ -> MethCall (js_name ~global_attrs s)
| Unit _ -> MethCall (js_name ~global_attrs s)
| _ -> PropGet (js_name ~global_attrs s)
end
let parse_attr ~global_attrs (s, loc, auto) attribute =
let opt_name ?(prefix = "") ?(capitalize = false) () =
match attribute.attr_payload with
| PStr [] ->
begin match check_prefix ~prefix s with
| None | Some "" -> error loc (Implicit_name prefix)
| Some s
gitextract_fdgla5ae/
├── .github/
│ ├── dependabot.yml
│ └── workflows/
│ └── workflow.yml
├── .gitignore
├── .ocp-indent
├── CHANGES.md
├── CLASSES.md
├── IMPLGEN.md
├── INSTALL_AND_USE.md
├── LICENSE
├── LOW_LEVEL_BINDING.md
├── Makefile
├── NAMING.md
├── NODE_RUNTIME_BINDINGS.md
├── PPX.md
├── README.md
├── TODO.md
├── TYPES.md
├── VALUES.md
├── dune
├── dune-project
├── examples/
│ ├── calc/
│ │ ├── calc.html
│ │ ├── calc.ml
│ │ └── dune
│ ├── misc/
│ │ ├── dune
│ │ ├── jquery.mli
│ │ ├── js_date.mli
│ │ ├── js_str.mli
│ │ ├── test_jquery.html
│ │ └── test_jquery.ml
│ └── test/
│ ├── dune
│ ├── main.html
│ ├── main.ml
│ └── test_bindings.mli
├── gen_js_api.opam
├── lib/
│ ├── dune
│ ├── ojs.ml
│ ├── ojs.mli
│ ├── ojs_exn.ml
│ ├── ojs_exn.mli
│ ├── ojs_runtime.js
│ └── ojs_runtime_stubs.c
├── node-test/
│ ├── bindings/
│ │ ├── arrays.mli
│ │ ├── buffer.mli
│ │ ├── console.mli
│ │ ├── container.ml
│ │ ├── container.mli
│ │ ├── dune
│ │ ├── errors.mli
│ │ ├── expected/
│ │ │ ├── arrays.ml
│ │ │ ├── buffer.ml
│ │ │ ├── console.ml
│ │ │ ├── errors.ml
│ │ │ ├── fs.ml
│ │ │ ├── global.ml
│ │ │ ├── imports.ml
│ │ │ ├── number.ml
│ │ │ ├── path.ml
│ │ │ ├── process.ml
│ │ │ └── promise.ml
│ │ ├── fs.mli
│ │ ├── global.mli
│ │ ├── imports.js
│ │ ├── imports.mli
│ │ ├── imports.wat
│ │ ├── number.mli
│ │ ├── path.mli
│ │ ├── process.mli
│ │ └── promise.mli
│ ├── runtime_primitives/
│ │ ├── bindings.mli
│ │ ├── dune
│ │ ├── example.ml
│ │ ├── imports.js
│ │ └── imports.wat
│ └── test1/
│ ├── dune
│ ├── recursive.js
│ ├── recursive.mli
│ └── test.ml
├── ojs.opam
├── ojs.opam.template
├── ppx-driver/
│ ├── dune
│ └── gen_js_api_ppx_driver.ml
├── ppx-lib/
│ ├── dune
│ ├── gen_js_api_ppx.ml
│ └── gen_js_api_ppx.mli
├── ppx-standalone/
│ ├── dune
│ ├── gen_js_api.ml
│ └── gen_js_api.mli
└── ppx-test/
├── binding.mli
├── binding_automatic.mli
├── binding_explicitly_automatic.mli
├── binding_manual.mli
├── dune
├── expected/
│ ├── binding.ml
│ ├── binding_automatic.ml
│ ├── extension.ml
│ ├── first_class_modules.ml
│ ├── issues.ml
│ ├── issues_mli.ml
│ ├── modules.ml
│ ├── recursive_modules.ml
│ ├── scoped.ml
│ ├── types.ml
│ └── union_and_enum.ml
├── extension.ml
├── first_class_modules.mli
├── issues.ml
├── issues_mli.mli
├── modules.mli
├── ppx/
│ ├── dune
│ └── main.ml
├── recursive_modules.mli
├── scoped.mli
├── types.ml
└── union_and_enum.mli
SYMBOL INDEX (6 symbols across 3 files)
FILE: lib/ojs_runtime.js
function caml_ojs_wrap_fun_arguments (line 3) | function caml_ojs_wrap_fun_arguments(f) {
function caml_ojs_iterate_properties (line 11) | function caml_ojs_iterate_properties(o, f) {
FILE: lib/ojs_runtime_stubs.c
function caml_ojs_wrap_fun_arguments (line 3) | void caml_ojs_wrap_fun_arguments () {
function caml_ojs_iterate_properties (line 7) | void caml_ojs_iterate_properties () {
FILE: node-test/test1/recursive.js
function Foo (line 5) | function Foo(name) {
function Bar (line 26) | function Bar(name) {
Condensed preview — 114 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (381K chars).
[
{
"path": ".github/dependabot.yml",
"chars": 111,
"preview": "version: 2\nupdates:\n - package-ecosystem: github-actions\n directory: /\n schedule:\n interval: daily\n"
},
{
"path": ".github/workflows/workflow.yml",
"chars": 1665,
"preview": "name: Builds, tests & co\n\non:\n - push\n - pull_request\n\npermissions: read-all\n\njobs:\n build:\n strategy:\n fail-"
},
{
"path": ".gitignore",
"chars": 61,
"preview": "gen_js_api.install\nojs.install\n*.merlin\n_build\n_opam\n.vscode\n"
},
{
"path": ".ocp-indent",
"chars": 32,
"preview": "match_clause=4\nstrict_with=auto\n"
},
{
"path": "CHANGES.md",
"chars": 3354,
"preview": "Changelog\n=========\n\nUnreleased\n----------\n\n- Support for binding to js_of_ocaml runtime primitives via `@`-prefixed pay"
},
{
"path": "CLASSES.md",
"chars": 3748,
"preview": "Class wrapping in gen_js_api\n============================\n\ngen_js_api can bind JavaScript objects into OCaml abstract ty"
},
{
"path": "IMPLGEN.md",
"chars": 3457,
"preview": "gen_js_api: generate implementations from interfaces\n====================================================\n\nThe primary o"
},
{
"path": "INSTALL_AND_USE.md",
"chars": 2103,
"preview": "gen_js_api: installation and usage instructions\n===============================================\n\n\nDependencies\n---------"
},
{
"path": "LICENSE",
"chars": 1073,
"preview": "The MIT License (MIT)\n\nCopyright 2015 by LexiFi.\n\nPermission is hereby granted, free of charge, to any person obtaining\n"
},
{
"path": "LOW_LEVEL_BINDING.md",
"chars": 2533,
"preview": "gen_js_api: low-level binding to JavaScript\n===========================================\n\nThe code generated by gen_js_ap"
},
{
"path": "Makefile",
"chars": 872,
"preview": "# The package gen_js_api is released under the terms of an MIT-like license.\n# See the attached LICENSE file.\n# Copyrigh"
},
{
"path": "NAMING.md",
"chars": 1044,
"preview": "gen_js_api: default naming convention\n=====================================\n\nJavaScript names corresponding to bound com"
},
{
"path": "NODE_RUNTIME_BINDINGS.md",
"chars": 6188,
"preview": "# Binding Node.js Modules with Runtime Primitives\n\nThis guide shows how to use the new runtime primitive support in `gen"
},
{
"path": "PPX.md",
"chars": 1647,
"preview": "gen_js_api: ppx mode\n====================\n\nWhile the primary mode of operation for gen_js_api is to generate an\n.ml file"
},
{
"path": "README.md",
"chars": 3796,
"preview": "gen_js_api: easy OCaml bindings for JavaScript libraries\n========================================================\n\n[![Bu"
},
{
"path": "TODO.md",
"chars": 2008,
"preview": "TODO list for gen_js_api\n========================\n\n- Create reasonably complete bindings for JavaScript's stdlib\n (stri"
},
{
"path": "TYPES.md",
"chars": 16549,
"preview": "Types supported in gen_js_api\n=============================\n\nJS-able types\n-------------\n\nA JS-able type is an OCaml typ"
},
{
"path": "VALUES.md",
"chars": 16056,
"preview": "Value bindings in gen_js_api\n============================\n\n\nSupported forms\n---------------\n\n- Method call:\n\n ```ocaml\n"
},
{
"path": "dune",
"chars": 115,
"preview": "(env\n (dev\n (flags (:standard))))\n\n(deprecated_library_name\n (old_public_name gen_js_api)\n (new_public_name ojs))\n"
},
{
"path": "dune-project",
"chars": 1102,
"preview": "(lang dune 3.17)\n(name gen_js_api)\n(version 1.1.7)\n\n(maintainers \"Alain Frisch <alain.frisch@lexifi.com>\")\n(authors\n \"Al"
},
{
"path": "examples/calc/calc.html",
"chars": 119,
"preview": "<html>\n <head>\n <title>Calculator</title>\n </head>\n <body>\n <script src=\"calc.js\"></script>\n </body>\n</html>\n"
},
{
"path": "examples/calc/calc.ml",
"chars": 4604,
"preview": "module Element = [%js:\n type t\n\n val t_of_js: Ojs.t -> t\n\n val append_child: t -> t -> unit [@@js.call]\n\n val set_at"
},
{
"path": "examples/calc/dune",
"chars": 231,
"preview": "(executables\n (names calc)\n (libraries ojs)\n (preprocess\n (pps gen_js_api.ppx))\n (modes js))\n\n(rule\n (targets calc.js)\n"
},
{
"path": "examples/misc/dune",
"chars": 544,
"preview": "(executables\n (names test_jquery)\n (libraries ojs)\n (preprocess\n (pps gen_js_api.ppx))\n (modes js))\n\n(rule\n (targets jq"
},
{
"path": "examples/misc/jquery.mli",
"chars": 4852,
"preview": "(* The gen_js_api is released under the terms of an MIT-like license. *)\n(* See the attached LICENSE file. "
},
{
"path": "examples/misc/js_date.mli",
"chars": 2471,
"preview": "(* The gen_js_api is released under the terms of an MIT-like license. *)\n(* See the attached LICENSE file. "
},
{
"path": "examples/misc/js_str.mli",
"chars": 2332,
"preview": "(* The gen_js_api is released under the terms of an MIT-like license. *)\n(* See the attached LICENSE file. "
},
{
"path": "examples/misc/test_jquery.html",
"chars": 322,
"preview": "<html>\n <head>\n </head>\n <body>\n <span class=\"tofill\">One</span>\n <span class=\"tofill\">Two</span>\n <div id=\""
},
{
"path": "examples/misc/test_jquery.ml",
"chars": 2281,
"preview": "(* The gen_js_api is released under the terms of an MIT-like license. *)\n(* See the attached LICENSE file. "
},
{
"path": "examples/test/dune",
"chars": 330,
"preview": "(executables\n (names main)\n (libraries ojs)\n (preprocess\n (pps gen_js_api.ppx))\n (modes js))\n\n(rule\n (targets test_bind"
},
{
"path": "examples/test/main.html",
"chars": 3067,
"preview": "<html>\n <head>\n </head>\n <body>\n <div class=\"myClass\">Blabla</div>\n <canvas id=\"canvas\" width=\"200\" height=\"200"
},
{
"path": "examples/test/main.ml",
"chars": 9697,
"preview": "(* The gen_js_api is released under the terms of an MIT-like license. *)\n(* See the attached LICENSE file. "
},
{
"path": "examples/test/test_bindings.mli",
"chars": 7916,
"preview": "(* The gen_js_api is released under the terms of an MIT-like license. *)\n(* See the attached LICENSE file. "
},
{
"path": "gen_js_api.opam",
"chars": 1224,
"preview": "# This file is generated by dune, edit dune-project instead\nopam-version: \"2.0\"\nversion: \"1.1.7\"\nsynopsis: \"Easy OCaml b"
},
{
"path": "lib/dune",
"chars": 258,
"preview": "(library\n (public_name ojs)\n (synopsis \"Runtime support for gen_js_api\")\n (libraries js_of_ocaml-compiler.runtime)\n (wra"
},
{
"path": "lib/ojs.ml",
"chars": 5085,
"preview": "(* The gen_js_api is released under the terms of an MIT-like license. *)\n(* See the attached LICENSE file. "
},
{
"path": "lib/ojs.mli",
"chars": 4784,
"preview": "(* The gen_js_api is released under the terms of an MIT-like license. *)\n(* See the attached LICENSE file. "
},
{
"path": "lib/ojs_exn.ml",
"chars": 763,
"preview": "(* The gen_js_api is released under the terms of an MIT-like license. *)\n(* See the attached LICENSE file. "
},
{
"path": "lib/ojs_exn.mli",
"chars": 402,
"preview": "(* The gen_js_api is released under the terms of an MIT-like license. *)\n(* See the attached LICENSE file. "
},
{
"path": "lib/ojs_runtime.js",
"chars": 399,
"preview": "//Provides: caml_ojs_wrap_fun_arguments\n//Requires: caml_js_wrap_callback\nfunction caml_ojs_wrap_fun_arguments(f) {\n re"
},
{
"path": "lib/ojs_runtime_stubs.c",
"chars": 317,
"preview": "#include <stdlib.h>\n#include <stdio.h>\nvoid caml_ojs_wrap_fun_arguments () {\n fprintf(stderr, \"Unimplemented JavaScript"
},
{
"path": "node-test/bindings/arrays.mli",
"chars": 1115,
"preview": "module JsArray (E: Ojs.T): sig\n type t\n val t_to_js: t -> Ojs.t\n val t_of_js: Ojs.t -> t\n\n val create: unit -> t [@@"
},
{
"path": "node-test/bindings/buffer.mli",
"chars": 538,
"preview": "[@@@js.scope \"Buffer\"]\n\ntype t = private Ojs.t\nval t_of_js: Ojs.t -> t\nval t_to_js: t -> Ojs.t\n\nval alloc: int -> t[@@js"
},
{
"path": "node-test/bindings/console.mli",
"chars": 268,
"preview": "[@@@js.scope \"console\"]\n\nval log: 'a -> unit [@@js.global]\nval error: 'a -> unit [@@js.global]\n\nmodule T : sig\n val log"
},
{
"path": "node-test/bindings/container.ml",
"chars": 352,
"preview": "module StringMap = struct\n include Map.Make(String)\n\n let t_to_js ml2js l =\n let o = Ojs.empty_obj () in\n iter ("
},
{
"path": "node-test/bindings/container.mli",
"chars": 157,
"preview": "module StringMap : sig\n include Map.S with type key = string\n val t_to_js: ('a -> Ojs.t) -> 'a t -> Ojs.t\n val t_of_j"
},
{
"path": "node-test/bindings/dune",
"chars": 1951,
"preview": "(library\n (name node)\n (synopsis \"Bindings\")\n (libraries ojs)\n (preprocess\n (pps gen_js_api.ppx))\n (modes byte)\n (js_of"
},
{
"path": "node-test/bindings/errors.mli",
"chars": 342,
"preview": "module [@js.scope] Error : sig\n type t\n val t_to_js: t -> Ojs.t\n val t_of_js: Ojs.t -> t\n\n val create: string -> t ["
},
{
"path": "node-test/bindings/expected/arrays.ml",
"chars": 3244,
"preview": "[@@@js.dummy \"!! This code has been generated by gen_js_api !!\"]\n[@@@ocaml.warning \"-7-32-39\"]\nmodule JsArray(E:Ojs.T) ="
},
{
"path": "node-test/bindings/expected/buffer.ml",
"chars": 1884,
"preview": "[@@@js.dummy \"!! This code has been generated by gen_js_api !!\"]\n[@@@ocaml.warning \"-7-32-39\"]\ntype t = Ojs.t\nlet rec t_"
},
{
"path": "node-test/bindings/expected/console.ml",
"chars": 975,
"preview": "[@@@js.dummy \"!! This code has been generated by gen_js_api !!\"]\n[@@@ocaml.warning \"-7-32-39\"]\nlet log : 'a -> unit =\n "
},
{
"path": "node-test/bindings/expected/errors.ml",
"chars": 1128,
"preview": "[@@@js.dummy \"!! This code has been generated by gen_js_api !!\"]\n[@@@ocaml.warning \"-7-32-39\"]\nmodule Error =\n struct\n "
},
{
"path": "node-test/bindings/expected/fs.ml",
"chars": 4807,
"preview": "[@@@js.dummy \"!! This code has been generated by gen_js_api !!\"]\n[@@@ocaml.warning \"-7-32-39\"]\nmodule Dirent =\n struct\n"
},
{
"path": "node-test/bindings/expected/global.ml",
"chars": 1173,
"preview": "[@@@js.dummy \"!! This code has been generated by gen_js_api !!\"]\n[@@@ocaml.warning \"-7-32-39\"]\ntype timeout_id = Ojs.t\nl"
},
{
"path": "node-test/bindings/expected/imports.ml",
"chars": 156,
"preview": "[@@@js.dummy \"!! This code has been generated by gen_js_api !!\"]\n[@@@ocaml.warning \"-7-32-39\"]\nlet path : Ojs.t = Jsoo_r"
},
{
"path": "node-test/bindings/expected/number.ml",
"chars": 4892,
"preview": "[@@@js.dummy \"!! This code has been generated by gen_js_api !!\"]\n[@@@ocaml.warning \"-7-32-39\"]\ntype t = Ojs.t\nlet rec t_"
},
{
"path": "node-test/bindings/expected/path.ml",
"chars": 2267,
"preview": "[@@@js.dummy \"!! This code has been generated by gen_js_api !!\"]\n[@@@ocaml.warning \"-7-32-39\"]\nlet sep : string = Ojs.st"
},
{
"path": "node-test/bindings/expected/process.ml",
"chars": 399,
"preview": "[@@@js.dummy \"!! This code has been generated by gen_js_api !!\"]\n[@@@ocaml.warning \"-7-32-39\"]\nlet env : string Containe"
},
{
"path": "node-test/bindings/expected/promise.ml",
"chars": 2467,
"preview": "[@@@js.dummy \"!! This code has been generated by gen_js_api !!\"]\n[@@@ocaml.warning \"-7-32-39\"]\nmodule UntypedPromise =\n "
},
{
"path": "node-test/bindings/fs.mli",
"chars": 1311,
"preview": "[@@@js.scope \"@node_fs.promises\"]\n\nmodule Dirent : sig\n type t = Ojs.t\n val t_of_js: Ojs.t -> t\n val t_to_js: t -> Oj"
},
{
"path": "node-test/bindings/global.mli",
"chars": 450,
"preview": "type timeout_id\nval timeout_id_to_js: timeout_id -> Ojs.t\nval timeout_id_of_js: Ojs.t -> timeout_id\n\ntype interval_id\nva"
},
{
"path": "node-test/bindings/imports.js",
"chars": 105,
"preview": "//Provides: node_path\nvar node_path = require('path');\n\n//Provides: node_fs\nvar node_fs = require('fs');\n"
},
{
"path": "node-test/bindings/imports.mli",
"chars": 43,
"preview": "val path: Ojs.t [@@js.global \"@node_path\"]\n"
},
{
"path": "node-test/bindings/imports.wat",
"chars": 125,
"preview": "\n(global (export \"_node_path\") (import \"js\" \"node_path\") anyref)\n(global (export \"_node_fs\") (import \"js\" \"node_fs\") any"
},
{
"path": "node-test/bindings/number.mli",
"chars": 1238,
"preview": "type t = private Ojs.t\n\nval toString: t -> ?radix:int -> unit -> float [@@js.call]\nval toFixed: t -> ?fractionDigits:int"
},
{
"path": "node-test/bindings/path.mli",
"chars": 466,
"preview": "[@@@js.scope Imports.path]\n\nval sep: string [@@js.global]\nval dirname: string -> string [@@js.global]\nval extname: strin"
},
{
"path": "node-test/bindings/process.mli",
"chars": 118,
"preview": "[@@@js.scope \"process\"]\n\nval env : string Container.StringMap.t [@@js.global]\nval version: string option [@@js.global]"
},
{
"path": "node-test/bindings/promise.mli",
"chars": 2391,
"preview": "module UntypedPromise : sig\n\n type t = private Ojs.t\n val t_to_js: t -> Ojs.t\n val t_of_js: Ojs.t -> t\n\n [@@@js.stop"
},
{
"path": "node-test/runtime_primitives/bindings.mli",
"chars": 640,
"preview": "module [@js.scope \"@node_fs\"] Fs : sig\n val write_file_sync : string -> string -> unit [@@js.global \"writeFileSync\"]\n "
},
{
"path": "node-test/runtime_primitives/dune",
"chars": 521,
"preview": "(rule\n (targets bindings.ml)\n (deps bindings.mli)\n (action\n (run gen_js_api %{deps})))\n\n(executable\n (name example)\n (l"
},
{
"path": "node-test/runtime_primitives/example.ml",
"chars": 984,
"preview": "open Bindings\n\nlet initial_content = \"Hello, Node.js!\"\nlet appended_line = \"\\nAppending a new line.\"\nlet encoding = \"utf"
},
{
"path": "node-test/runtime_primitives/imports.js",
"chars": 251,
"preview": "'use strict';\n\n//Provides: node_path\nvar node_path = require('path');\n\n//Provides: node_fs\nvar node_fs = require('fs');\n"
},
{
"path": "node-test/runtime_primitives/imports.wat",
"chars": 264,
"preview": "(global (export \"_node_path\") (import \"js\" \"node_path\") anyref)\n(global (export \"_node_fs\") (import \"js\" \"node_fs\") anyr"
},
{
"path": "node-test/test1/dune",
"chars": 511,
"preview": "(executable\n (name test)\n (libraries ojs node)\n (preprocess\n (pps gen_js_api.ppx))\n (modes js wasm)\n (js_of_ocaml\n (ja"
},
{
"path": "node-test/test1/recursive.js",
"chars": 673,
"preview": "\nvar Foo = /*#__PURE__*/function () {\n \"use strict\";\n\n function Foo(name) {\n this.name = name;\n }\n\n var _proto = "
},
{
"path": "node-test/test1/recursive.mli",
"chars": 506,
"preview": "module [@js.scope \"Foo\"] rec Foo : sig\n type t = private Ojs.t\n val t_of_js: Ojs.t -> t\n val t_to_js: t -> Ojs.t\n va"
},
{
"path": "node-test/test1/test.ml",
"chars": 8233,
"preview": "(* The gen_js_api is released under the terms of an MIT-like license. *)\n(* See the attached LICENSE file. "
},
{
"path": "ojs.opam",
"chars": 777,
"preview": "# This file is generated by dune, edit dune-project instead\nopam-version: \"2.0\"\nversion: \"1.1.7\"\nsynopsis: \"Runtime Libr"
},
{
"path": "ojs.opam.template",
"chars": 104,
"preview": "build: [\n [\"dune\" \"subst\"] {dev}\n [\"dune\" \"build\" \"-p\" name \"-j\" jobs \"@install\" \"@doc\" {with-doc}]\n]\n"
},
{
"path": "ppx-driver/dune",
"chars": 244,
"preview": "(library\n (name gen_js_api_ppx_driver)\n (public_name gen_js_api.ppx)\n (synopsis \"Syntactic support for gen_js_api\")\n (li"
},
{
"path": "ppx-driver/gen_js_api_ppx_driver.ml",
"chars": 1995,
"preview": "let check_attributes_with_ppxlib = false\nlet check_locations_with_ppxlib = false\n\nlet () =\n if check_attributes_with_pp"
},
{
"path": "ppx-lib/dune",
"chars": 164,
"preview": "(library\n (name gen_js_api_ppx)\n (public_name gen_js_api.lib)\n (libraries compiler-libs.common ppxlib)\n (ppx_runtime_lib"
},
{
"path": "ppx-lib/gen_js_api_ppx.ml",
"chars": 86991,
"preview": "(* The gen_js_api is released under the terms of an MIT-like license. *)\n(* See the attached LICENSE file. "
},
{
"path": "ppx-lib/gen_js_api_ppx.mli",
"chars": 732,
"preview": "(* The gen_js_api is released under the terms of an MIT-like license. *)\n(* See the attached LICENSE file. "
},
{
"path": "ppx-standalone/dune",
"chars": 233,
"preview": "(executables\n (names gen_js_api)\n (public_names gen_js_api)\n (package gen_js_api)\n (libraries compiler-libs.common ppxli"
},
{
"path": "ppx-standalone/gen_js_api.ml",
"chars": 375,
"preview": "(* The gen_js_api is released under the terms of an MIT-like license. *)\n(* See the attached LICENSE file. "
},
{
"path": "ppx-standalone/gen_js_api.mli",
"chars": 295,
"preview": "(* The gen_js_api is released under the terms of an MIT-like license. *)\n(* See the attached LICENSE file. "
},
{
"path": "ppx-test/binding.mli",
"chars": 1561,
"preview": "(* The gen_js_api is released under the terms of an MIT-like license. *)\n(* See the attached LICENSE file. "
},
{
"path": "ppx-test/binding_automatic.mli",
"chars": 1051,
"preview": "(* The gen_js_api is released under the terms of an MIT-like license. *)\n(* See the attached LICENSE file. "
},
{
"path": "ppx-test/binding_explicitly_automatic.mli",
"chars": 1176,
"preview": "(* The gen_js_api is released under the terms of an MIT-like license. *)\n(* See the attached LICENSE file. "
},
{
"path": "ppx-test/binding_manual.mli",
"chars": 1419,
"preview": "(* The gen_js_api is released under the terms of an MIT-like license. *)\n(* See the attached LICENSE file. "
},
{
"path": "ppx-test/dune",
"chars": 3244,
"preview": "(rule\n (targets extension.ml.result)\n (deps extension.ml)\n (action\n (run ppx/main.exe --impl %{deps} -o %{targets})))\n\n"
},
{
"path": "ppx-test/expected/binding.ml",
"chars": 3470,
"preview": "[@@@js.dummy \"!! This code has been generated by gen_js_api !!\"]\n[@@@ocaml.warning \"-7-32-39\"]\nmodule M =\n struct\n t"
},
{
"path": "ppx-test/expected/binding_automatic.ml",
"chars": 5225,
"preview": "[@@@js.dummy \"!! This code has been generated by gen_js_api !!\"]\n[@@@ocaml.warning \"-7-32-39\"]\n[@@@warning \"-22\"]\nmodule"
},
{
"path": "ppx-test/expected/extension.ml",
"chars": 143,
"preview": "let _ = Ojs.int_to_js\nlet _ =\n fun (x2 : int -> int) ->\n Ojs.fun_to_js 1\n (fun (x3 : Ojs.t) -> Ojs.int_to_js (x"
},
{
"path": "ppx-test/expected/first_class_modules.ml",
"chars": 6957,
"preview": "[@@@js.dummy \"!! This code has been generated by gen_js_api !!\"]\n[@@@ocaml.warning \"-7-32-39\"]\nmodule Console =\n struct"
},
{
"path": "ppx-test/expected/issues.ml",
"chars": 9740,
"preview": "module Issue116 : sig type t end =\n ((struct\n [@@@js.dummy \"!! This code has been generated by gen_js_api !!\"]\n "
},
{
"path": "ppx-test/expected/issues_mli.ml",
"chars": 433,
"preview": "[@@@js.dummy \"!! This code has been generated by gen_js_api !!\"]\n[@@@ocaml.warning \"-7-32-39\"]\nmodule Issue144 =\n struc"
},
{
"path": "ppx-test/expected/modules.ml",
"chars": 643,
"preview": "[@@@js.dummy \"!! This code has been generated by gen_js_api !!\"]\n[@@@ocaml.warning \"-7-32-39\"]\nmodule Event =\n struct\n "
},
{
"path": "ppx-test/expected/recursive_modules.ml",
"chars": 1635,
"preview": "[@@@js.dummy \"!! This code has been generated by gen_js_api !!\"]\n[@@@ocaml.warning \"-7-32-39\"]\nmodule rec\n Foo:sig\n "
},
{
"path": "ppx-test/expected/scoped.ml",
"chars": 4040,
"preview": "[@@@js.dummy \"!! This code has been generated by gen_js_api !!\"]\n[@@@ocaml.warning \"-7-32-39\"]\n[@@@warning \"-22\"]\nmodule"
},
{
"path": "ppx-test/expected/types.ml",
"chars": 28401,
"preview": "type 'a of_js = Ojs.t -> 'a\ntype 'a to_js = 'a -> Ojs.t\n[@@@ocaml.text \" JS-able types \"]\nlet _ : string of_js = Ojs.str"
},
{
"path": "ppx-test/expected/union_and_enum.ml",
"chars": 17678,
"preview": "[@@@js.dummy \"!! This code has been generated by gen_js_api !!\"]\n[@@@ocaml.warning \"-7-32-39\"]\ntype enum_int =\n | Enum_"
},
{
"path": "ppx-test/extension.ml",
"chars": 52,
"preview": "\nlet _ = [%js.of: int]\nlet _ = [%js.of: int -> int]\n"
},
{
"path": "ppx-test/first_class_modules.mli",
"chars": 1933,
"preview": "module[@js.scope \"console\"] Console: sig\n val log: (module[@js] Ojs.T with type t = 'a) -> 'a -> unit [@@js.global \"log"
},
{
"path": "ppx-test/issues.ml",
"chars": 1843,
"preview": "module Issue116 = [%js: type t]\nmodule Issue117 = [%js:\n module T: sig\n val log: 'a -> unit [@@js.global]\n val lo"
},
{
"path": "ppx-test/issues_mli.mli",
"chars": 94,
"preview": "module Issue144: sig\n type t\n val f: t -> (args:int -> int [@js.dummy]) [@@js.call \"f\"]\nend\n"
},
{
"path": "ppx-test/modules.mli",
"chars": 283,
"preview": "module Event: sig\n type t = private Ojs.t\n val t_to_js: t -> Ojs.t\n val t_of_js: Ojs.t -> t\nend\n\nmodule Foo: sig\n mo"
},
{
"path": "ppx-test/ppx/dune",
"chars": 68,
"preview": "(executable\n (name main)\n (libraries ppxlib gen_js_api_ppx_driver))\n"
},
{
"path": "ppx-test/ppx/main.ml",
"chars": 102,
"preview": "\n(* To run as a standalone binary, run the registered drivers *)\nlet () = Ppxlib.Driver.standalone ()\n"
},
{
"path": "ppx-test/recursive_modules.mli",
"chars": 506,
"preview": "module [@js.scope \"Foo\"] rec Foo : sig\n type t = private Ojs.t\n val t_of_js: Ojs.t -> t\n val t_to_js: t -> Ojs.t\n va"
},
{
"path": "ppx-test/scoped.mli",
"chars": 897,
"preview": "(* The gen_js_api is released under the terms of an MIT-like license. *)\n(* See the attached LICENSE file. "
},
{
"path": "ppx-test/types.ml",
"chars": 5166,
"preview": "\ntype 'a of_js = Ojs.t -> 'a\ntype 'a to_js = 'a -> Ojs.t\n\n(** JS-able types *)\n\nlet _ : string of_js = [%js.to: string]\n"
},
{
"path": "ppx-test/union_and_enum.mli",
"chars": 4808,
"preview": "type enum_int =\n | Enum_int_0 [@js 0]\n | Enum_int_1 [@js 1]\n | Enum_int_other of int [@js.default]\n [@@js.enum]\n\ntyp"
}
]
About this extraction
This page contains the full source code of the LexiFi/gen_js_api GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 114 files (352.5 KB), approximately 108.8k tokens, and a symbol index with 6 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.