Separate compilation
The js_of_ocaml compiler supports two compilation modes. Whole program compilation that compiles a bytecode executable to a single JavaScript file and separate compilation that compiles individual compilation units (.cmo) and libraries (.cma) to one or more javascript files.
Why use separate compilation
Separate compilation improves the overall compilation times and gives (many) incremental build opportunities.
Theses improvements come at the cost of bigger executable and slower runtime. In particular, separate compilation will disable most cross module and cross library optimisations and make the dead-code elimination almost useless.
Compilation scheme
Js_of_ocaml separate compilation is somewhat similar to the OCaml separate compilation with some differences.
The general idea is generate one JavaScript file containing the JavaScript runtime and JavaScript files for every compilation unit (or library) needed to run the program. One can then link (or "concatenate") all individual JavaScript files together, respecting the order induced by dependencies, into a single JavaScript file.
1. Build the runtime
To build a standalone runtime:
js_of_ocaml build-runtime -o my-runtime.js
Additional runtime files are sometime needed and can be passed on the command line. In particular, some packages/libraries come with their own runtime file.
As an example, compiling a program that has a dependency on base will require the runtime.js} file provided by base.
js_of_ocaml --runtime-only -o my-runtime.js PATH_TO_BASE_LIB/runtime.js dummy
If js_of_ocaml is compiled with findlib support, one can also use the following syntax +{findlib-package}/{javascript-file} .
js_of_ocaml --runtime-only -o my-runtime.js +base/runtime.js dummy
2. Build compilation units
In addition to bytecode executables, the js_of_ocaml compiler can process compilation units (.cmo) and libraries (.cma).
One can specify the name of the generated file with -o. By default, the name will be inferred from the input file.
By default, the js_of_ocaml compiler will generate a single JavaScript file when compiling an ocaml library (.cma). One can choose to generate one file per compilation unit by passing the --keep-unit-names flag. In that case, the name will be inferred from the compilation unit name and -o will be understood as a destination directory.
js_of_ocaml modname.cmo #generates modname.js js_of_ocaml libname.cma #generates libname.js js_of_ocaml libname.cma -o name.js #generates name.js mkdir libname js_of_ocaml libname.cma --keep-unit-names -o libname/ #generates libname/Unit1.js libname/Unit2.js
3. Link
The final step is to link all individual JavaScript files into a single one. The link command of the js_of_ocaml-compiler takes a list of JavaScript filename as input and concatenates them all (merging source-maps together if needed).
js_of_ocaml link my-runtime.js stdlib.js libname.js modname.js -o myexe.js
Note that, unlike the ocaml compiler, the ocaml stdlib need to be passed explicitly (here, {stdlib.js} is the result of compiling {stdlib.cma}).
Support in dune
Support for js_of_ocaml separate compilation has been added to dune (previously jbuilder). The separate compilation mode is selected when the build profile is dev, which is the default. There is currently no other way to control this behaviour.
Source-maps support
Separate compilation can be used together with source-map. The recommended way is to build all individual javascript files with inlined source-map by passing --source-map-inline to js_of_ocaml.
Note that other configurations have not been well tested and might require additional tuning.
Building Toplevel
Building a toplevel using separate compilation is not supported / has not been tested yet.