This page covers how to link JavaScript files with js_of_ocaml, including writing primitives and discovering runtime files.
The js_of_ocaml compiler accepts JavaScript files on the command-line. The main purpose is to provide (external) primitives needed by the bytecode program.
Most primitives from the standard library are already implemented and loaded by default.
Pass JavaScript files (must have ".js" extension) directly:
js_of_ocaml myruntime.js program.byte
OCaml libraries often require JavaScript runtime files. The js_of_ocaml compiler does not automatically locate these files - They must be passed explicitly on the command line.
Runtime files are discovered using Findlib's jsoo_runtime variable.
To list all JavaScript runtime files for a set of libraries:
ocamlfind query -format "%+(jsoo_runtime)" -r LIB1 LIB2 ... | grep -v "^$"
Example for base and time_now:
$ ocamlfind query -format "%+(jsoo_runtime)" -r base time_now | grep -v "^$" ~/.opam/4.14.0/lib/base/base_internalhash_types/runtime.js ~/.opam/4.14.0/lib/base/runtime.js ~/.opam/4.14.0/lib/time_now/runtime.js
$ export LIBS=base,time_now $ ocamlfind ocamlc -package $LIBS -linkpkg main.ml -o main.byte $ js_of_ocaml $(ocamlfind query -format "%+(jsoo_runtime)" -r $LIBS | grep -v "^$") main.byte
With dune, this is handled automatically.
User-defined primitives need to be implemented in a separate JavaScript file. See runtime representation for how OCaml values are represented in JavaScript. Primitives should be annotated with additional information.
Annotations are single line comments with the following syntax
//Provides: primitive_name [const|mutable]
//Requires: other_primitive[,another_primitive]*
//Version: version_constraint[,version_constraint]*
function primitive_name(arg1, arg2) {
// JavaScript implementation
}//Provides: name [modifier] - Declares a primitive:
const - No side effects (pure function)mutable - No side effects, but return value may be affected by other primitivesmutator (or empty) - May have side effects//Requires: name1, name2 - Lists primitives that must be loaded first
//Version: < 4.12.0 - OCaml version constraint
//Alias: other_name - Makes this primitive an alias for another
//Weakdef - Allows this primitive to be overridden by another definition
//Always - Always include this code, even if not referenced
//If: flag - Only include if flag is enabled (e.g., //If: effects)
//If: !flag - Only include if flag is disabled
//Deprecated: message - Mark primitive as deprecated
All JavaScript code after a //Provides annotation belongs to that primitive, until the next //Provides.
//Provides: my_log_primitive
//Requires: caml_jsstring_of_string
function my_log_primitive(msg) {
console.log(caml_jsstring_of_string(msg));
return 0; // Return unit
}OCaml side:
external my_log : string -> unit = "my_log_primitive"
let () = my_log "Hello from OCaml!"In your package's META file, use the jsoo_runtime variable:
# mypackage/META package "sublib" ( directory = "subdir" jsoo_runtime = "runtime.js" )
Files should be comma-separated and relative to the package directory. By convention, use runtime.js for single files.
Dune automatically handles jsoo_runtime declarations. In your dune file:
(library (name mylib) (js_of_ocaml (javascript_files runtime.js)))
Before dune's native js_of_ocaml support, the standard approach was to use linkopts(javascript) in META files:
# Old approach - deprecated package "sublib" ( directory = "subdir" linkopts(javascript) = "+mypackage/sublib/runtime.js" )
To list linkopts for libraries:
ocamlfind query -o-format -r -predicates javascript LIB1 LIB2 ...
When using linkopts, special path syntax is supported:
+package/file.js - Resolves to ${LIBDIR}/package/file.js+file.js - Resolves to js_of_ocaml-compiler's lib directoryNote: Dune generates META files compatible with both approaches but only uses jsoo_runtime internally.