Warning: Reason support is experimental. We are looking for beta-tester and contributors.

JavaScript primitives

This page covers how to link JavaScript files with js_of_ocaml, including writing primitives and discovering runtime files.

Overview

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.

Passing JavaScript files

Pass JavaScript files (must have ".js" extension) directly:

js_of_ocaml myruntime.js program.byte

Finding runtime files

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.

List required runtime files

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

Complete manual compilation example

$ 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.

Writing JavaScript primitives

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.

Annotation syntax

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
}

Annotations explained

//Provides: name [modifier] - Declares a primitive:

  • const - No side effects (pure function)
  • mutable - No side effects, but return value may be affected by other primitives
  • mutator (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)

//Ifnot: 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.

Example: custom primitive

//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!"

Declaring runtime files in packages

Using jsoo_runtime

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 integration

Dune automatically handles jsoo_runtime declarations. In your dune file:

(library
 (name mylib)
 (js_of_ocaml
  (javascript_files runtime.js)))

Deprecated: linkopts(javascript)

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 ...

Findlib path syntax (deprecated)

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 directory

Note: Dune generates META files compatible with both approaches but only uses jsoo_runtime internally.

See also