# Js\_of\_ocaml and JavaScript modules

**URL:** https://discuss.ocaml.org/t/js-of-ocaml-and-javascript-modules/7446
**Category:** Ecosystem
**Created:** [March 11, 2021, 2:49am UTC](https://discuss.ocaml.org/t/js-of-ocaml-and-javascript-modules/7446 "2021-03-11T02:49:12Z")
**Posts on this page:** 8
**Page:** 1

<div class="post-metadata">

### Author: ![joelb](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/joelb/32/1996_2.png) [@joelb](https://discuss.ocaml.org/u/joelb)
#### Post date: [March 11, 2021, 2:49am UTC](https://discuss.ocaml.org/t/js-of-ocaml-and-javascript-modules/7446/1 "2021-03-11T02:49:12Z")

</div>

Hi all, I’m writing to ask for advice re how to bind a library that uses JavaScript modules.

Full context:

- I’m working on writing JSOO bindings for [CodeMirror 6](https://codemirror.net/6/), which is [distributed using EcmaScript modules](https://codemirror.net/6/examples/bundle/). In the JS world, you’d use a “bundler” to link all the modules into a single file.
- I’m using Daniel Bünzli’s [brr](https://erratique.ch/software/brr/doc/index.html) library.
- My main concerns are twofold: (1) I’d like users to only have to pay for the modules they use, in other words, if you don’t use the `@codemirror/closebrackets` module, it shouldn’t appear in the JSOO output. (2) Ease of use – I’d like the library to just work for my users.

What’s the nicest way to do this? Does anyone have examples of doing the same for other JavaScript module-based libraries?

---

<div class="post-metadata">

### Author: ![dbuenzli](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/dbuenzli/32/18_2.png) [@dbuenzli](https://discuss.ocaml.org/u/dbuenzli)
#### Post date: [March 11, 2021, 7:08am UTC](https://discuss.ocaml.org/t/js-of-ocaml-and-javascript-modules/7446/2 "2021-03-11T07:08:17Z")

</div>

`CodeMirror` is certainly worth binding to as it handles a lot of browser mess – in fact if I had time for hacks I’d use it for `brr`'s OCaml console.

I’m not fully aware of all the JavaScript module bundling/packaging opportunities and mecanisms but there’s a lack of conventions in the `js_of_ocaml` world to enable good interop with the JavaScript ecosystem **S**. Had we good conventions and a bit of work in `opam` we could certainly get a nice `depext` system like we have for C. In any case with `js_of_ocaml` your bundler (that `rollup`, `webpack`, whatever thing) is `js_of_ocaml link`.

I never did that so that won’t be precise and there may be better ways. But basically what I would do in your case is bundle `CodeMirror` in the project (automate that step) then map JavaScript modules on OCaml _libraries_.

In each library directory have the corresponding JavaScript module `.js` file and the JavaScript glue code to include it (if needed, not exactly up-to-date with how JavaScript modules work), and have these Javascript files be linked whenever the library is.

For the last step I can’t find any documentation about it in `js_of_ocaml` :–( but this happens via ocamlfind `META` files. In each library you should add a line:

```auto
linkopts(javascript) = "+$(LIBRARY_DIR)/module_glue.js +$(LIBRARY_DIR)/module.js`

```

where `$(LIBRARY_DIR)` is the relative directory in which the library resides w.r.t. to `opam var lib`.

I think that with this both concerns 1) and 2) would be solved.

---

<div class="post-metadata">

### Author: ![joelb](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/joelb/32/1996_2.png) [@joelb](https://discuss.ocaml.org/u/joelb)
#### Post date: [April 24, 2021, 5:12pm UTC](https://discuss.ocaml.org/t/js-of-ocaml-and-javascript-modules/7446/3 "2021-04-24T17:12:39Z")

</div>

I wanted to follow up on this. I didn’t make much progress but want to document where I got stuck.

There are a few choices to make. First, to use dune or not. In @dbuenzli’s advice, he doesn’t use dune, but plain js\_of\_ocaml / META files. I’ve been using dune, like it a lot, and am not so familiar with META files / findlib, so I prefer to use dune if possible (note that where I got stuck applies to both the dune approach and the META approach). Let’s explore what this looks like using dune.

Here’s a simple dune file I’ll try using for the view:

> <https://github.com/joelburget/codemirror-ocaml/blob/main/view/dune>

If we try building with `(js_of_ocaml (javascript_files codemirror-view.js))`, we get:

```auto
 js_of_ocaml example/index.bc.runtime.js (exit 1)
(cd _build/default/example && /Users/joel/.opam/4.12.0/bin/js_of_ocaml build-runtime --pretty --source-map-inline -o index.bc.runtime.js ../view/codemirror-view.js)
/Users/joel/.opam/4.12.0/bin/js_of_ocaml: Error: cannot parse file "/Users/joel/code/ocaml/codemirror/_build/default/view/codemirror-view.js" (orig:"/Users/joel/code/ocaml/codemirror/_build/default/view/codemirror-view.js" from l:1, c:7)

```

`codemirror-view.js` is the file directly from the CodeMirror source on NPM. I guess it’s understandable that it doesn’t quite work because it uses JS modules. Here’s the first line:

```auto
import { MapMode, Text as Text$1, Facet, ChangeSet, Transaction, EditorSelection, CharCategory, EditorState, Prec, StateEffect, combineConfig } from '@codemirror/state';

```

Fine, let’s use Babel to transform this to ES5 (`npx babel codemirror-view.js --out-file codemirror-view-babel.js --presets @babel/preset-env`). We also change the `js_of_ocaml` clause to `(js_of_ocaml (javascript_files codemirror-view-babel.js))`:

```auto
 js_of_ocaml example/index.bc.runtime.js (exit 1)
(cd _build/default/example && /Users/joel/.opam/4.12.0/bin/js_of_ocaml build-runtime --pretty --source-map-inline -o index.bc.runtime.js ../view/codemirror-view-babel.js)
/Users/joel/.opam/4.12.0/bin/js_of_ocaml: Error: cannot parse file "/Users/joel/code/ocaml/codemirror/_build/default/view/codemirror-view-babel.js" (orig:"/Users/joel/code/ocaml/codemirror/_build/default/view/codemirror-view-babel.js" from l:236, c:12)

```

This time, the js\_of\_ocaml parser trips on a getter:

```auto
    dom.focus(preventScrollSupported == null ? {
        get preventScroll() {
            preventScrollSupported = { preventScroll: true };
            return true;
        }
    } : undefined);

```

I was a bit surprised to see this in the Babel output, but [according to MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/get) this syntax is in ES2015, so I guess it’s fine. I couldn’t find any Babel preset that doesn’t output a getter. So I guess we’re stuck here.

Aside: Someone else hit the [same problem](https://github.com/ocsigen/js_of_ocaml/issues/972) with js\_of\_ocaml. It also [can’t parse trailing commas](https://github.com/ocsigen/js_of_ocaml/issues/989). The advice on GitHub is “A workaround is to not pass that file to jsoo and bundle it later”. So the js\_of\_ocaml parser is a bit outdated. I’m curious, why does it parse JS in the first place?

Trying to take @hhugo’s advice from GitHub, I commented out the `js_of_ocaml` clause (as in the current checked-in dune file). This time the build works (great!), but the CodeMirror sources aren’t linked at all. This requires the user to figure out how to link / bundle them by hand, which doesn’t exactly “just work”.

So it seems like the crux of the problem is the js\_of\_ocaml parser’s incompatibility with JS modules. Has anyone figured out a way to work around this?

---

<div class="post-metadata">

### Author: ![joelb](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/joelb/32/1996_2.png) [@joelb](https://discuss.ocaml.org/u/joelb)
#### Post date: [April 24, 2021, 5:52pm UTC](https://discuss.ocaml.org/t/js-of-ocaml-and-javascript-modules/7446/4 "2021-04-24T17:52:32Z")

</div>

Note: I noticed that the getter appeared in only one place (everywhere else in `codemirror-view-babel.js` we see `get: function get() { ... }`). This seems to be a bug in one of Babel’s plugins? Anyway, since it’s only in one place I decided it’s acceptable to edit it out by hand. This leaves us with the [now-checked-in](https://github.com/joelburget/codemirror-ocaml/blob/main/view/codemirror-view-babel.js) `codemirror-view-babel.js`. js\_of\_ocaml can actually parse this one and `dune build` succeeds.

This is great, however we still see some module stuff in `codemirror-view-babel.js`: (I guess these are CommonJS modules?)

```auto
Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.drawSelection = drawSelection;
exports.highlightActiveLine = highlightActiveLine;
...

var _state = require("@codemirror/state");
var _styleMod = require("style-mod");

```

This takes us back to the original problem: what’s a sane way to make js\_of\_ocaml (slash OCaml modules) interact with JS (CommonJS?) modules? The snippet I posted above is still going to require a JavaScript bundler (to define `exports` and handle the `requires`). But I don’t think I want to bundle `codemirror-view.js`, `codemirror-text.js`, etc all separately (one for each OCaml module) because we’re going to be including the same dependencies N times. I’m not sure there’s any way to do this how @dbuenzli suggested, with separate OCaml libraries.

This is a bit unfortunate because it means I should use a JS bundler to bundle _all_ of the CodeMirror JS together before using that as the input to js\_of\_ocaml. I don’t expect that js\_of\_ocaml’s dead code elimination is smart enough to remove any of it. But at least it should work, I think.

---

<div class="post-metadata">

### Author: ![joelb](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/joelb/32/1996_2.png) [@joelb](https://discuss.ocaml.org/u/joelb)
#### Post date: [October 22, 2021, 4:21pm UTC](https://discuss.ocaml.org/t/js-of-ocaml-and-javascript-modules/7446/5 "2021-10-22T16:21:38Z")

</div>

Update (6 months later). I gave up on this approach and decided to create [bindings](https://github.com/joelburget/monaco-ocaml) to Microsoft’s Monaco editor instead. This turned out to be quite easy because it comes in bundled form (rather than as JavaScript modules).

---

<div class="post-metadata">

### Author: ![cemerick](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/cemerick/32/1383_2.png) [@cemerick](https://discuss.ocaml.org/u/cemerick)
#### Post date: [October 25, 2021, 6:43pm UTC](https://discuss.ocaml.org/t/js-of-ocaml-and-javascript-modules/7446/6 "2021-10-25T18:43:54Z")

</div>

\<offtopic\>  
Did you decide to use Monaco strictly because it was easier to use from OCaml/jsoo, or were there other considerations that motivated the switch? If the latter, I’d love to learn what you’d learned in since March. 🙂  
\</offtopic\>

---

<div class="post-metadata">

### Author: ![joelb](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/joelb/32/1996_2.png) [@joelb](https://discuss.ocaml.org/u/joelb)
#### Post date: [October 26, 2021, 1:20am UTC](https://discuss.ocaml.org/t/js-of-ocaml-and-javascript-modules/7446/7 "2021-10-26T01:20:11Z")

</div>

Honestly, it was almost entirely because Monaco is easy to use from jsoo. There’s not a lot of high-quality discussion comparing the two (the author of CodeMirror has a few comments comparing them [on HN](https://news.ycombinator.com/item?id=17858672), but from three years ago). I don’t have a huge amount of experience, but some tradeoffs as I understand them:

- Monaco is more rigid, Codemirror is more malleable. If you write a Monaco-based editor, it’s (by default) going to look like VS Code. Codemirror [editors](https://codemirror.net/6/examples/zebra/) [aren’t](https://codemirror.net/6/examples/panel/) as distinctive / recognizable. You can really [make it your own](https://codemirror.net/6/examples/decoration/).
- Monaco feels more like a complete editor you can customize; Codmirror, a set of components to assemble.
- Monaco comes with a lot of nice stuff (or at least the pieces you need to add these): syntax highlighting, autocomplete, suggestions, go to definition / symbol, etc. Codemirror has support for a [lot of this as well](https://codemirror.net/6/examples/lang-package/).
- Both are written in typescript.
- Both have fairly nice API docs. Compare [Monaco](https://microsoft.github.io/monaco-editor/api/index.html) vs [CodeMirror](https://codemirror.net/6/docs/ref/).
- CodeMirror supposedly works a little better on mobile due to using contenteditable.
- CodeMirror bundles everything together. Monaco uses an AMD loader / require.js to asynchronously load the bulk of its code.
- Monaco [uses web workers](https://github.com/microsoft/monaco-editor#faq) to make things smoother: “Language services create web workers to compute heavy stuff outside of the UI thread”.
- Monaco (VS Code) is developed by Microsoft and very popular. I have no concerns about its development future. Codemirror has an impressive list of [sponsors](https://codemirror.net/6/#sponsors). I’d bet it has less resources devoted to it but still doing pretty well.

Overall it feels quite close to me. If they were equally easy to work with I’d probably start with CodeMirror.

---

<div class="post-metadata">

### Author: ![Gopiandcode](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/gopiandcode/32/5346_2.png) [@Gopiandcode](https://discuss.ocaml.org/u/Gopiandcode)
#### Post date: [October 26, 2021, 2:42am UTC](https://discuss.ocaml.org/t/js-of-ocaml-and-javascript-modules/7446/8 "2021-10-26T02:42:36Z")

</div>

Another web-editor that could be used is the Ace-editor (see example of its use here: [https://source-academy.github.io/playground](https://source-academy.github.io/playground))

I’ve written some basic bindings to it using Brr here: [Ace editor Brr bindings ($2194985) · Snippets · Snippets · GitLab](https://gitlab.com/-/snippets/2194985)
