# How the documentation is generated

The Js_of_ocaml documentation published at <https://ocsigen.org/js_of_ocaml/> is
built entirely with **odoc** and themed with the Ocsigen site chrome by
[**wodoc**](https://github.com/ocsigen/wodoc) (an odoc driver). The same odoc
sources are also what ocaml.org renders.

## Sources

| What | Where | Format |
|---|---|---|
| Manual | [`manual/*.mld`](../manual) | odoc pages |
| Manual home / API overview | `manual/index.mld`, `manual/api.mld` | odoc pages |
| API | the `.mli` of every `js_of_ocaml*` package | odoc comments (native `{!…}` refs) |
| Interactive examples | `examples/`, `toplevel/examples/` | built by dune |
| Site configuration (nav, packages, …) | [`doc/wodoc`](wodoc) | wodoc config (S-expression) |

The manual used to be wikicreole (`manual/*.wiki`, rendered by `html_of_wiki`).
It has been converted to `.mld` (`wodoc convert`) so that a single odoc run
produces the manual **and** the API, and so the manual also appears on ocaml.org.
The left navigation, theme and per-page assembly used to be hand-written HTML and
Python scripts; they are now declared in [`doc/wodoc`](wodoc) and produced by
`wodoc build` (the page template, the version selector, the left navigation and
the cross-package reference resolution are all built in).

### Presentational markup (invisible on ocaml.org)

In the `.mld` manual, wodoc lets you add **CSS classes and layout containers** to
the documentation with `{%wodoc:…%}` markers (e.g. `{%wodoc:div class=…%}` … and
`{%wodoc:@ class=…%}` to put a class on the next element). Stock odoc does not
know the `wodoc` target and simply **drops it**, so the *same sources* render as
plain, semantic documentation on ocaml.org and as the themed website here — no
fork, nothing extra to maintain.

## Build

```
wodoc build --config doc/wodoc --label dev --out _doc-site/dev \
  --menu https://ocsigen.org/doc/menu.html
```

That single command (no wrapper script) does everything — `--menu` takes the
shared top menu's canonical copy on `ocsigen.org` (fetched for you). It:

1. runs `dune build @doc @doc-manual` — odoc HTML for the manual and the API of
   every `js_of_ocaml*` package in one run, plus the interactive examples
   (toplevel, boulderdash, …) into `_build/default/manual/files/`;
2. assembles every page into the Ocsigen site (shared header/menu/drawer, the
   version `<select>`, the left navigation declared in `doc/wodoc`), links the
   cross-package references to the sibling packages (`js_of_ocaml-lwt`, `-tyxml`,
   `-toplevel`), and ships the examples, the version redirect and the `latest`
   symlink.

Output goes to `<outdir>/<label>/` (default `_doc-site/<label>/`), laid out to
match `https://ocsigen.org/js_of_ocaml/<label>/`. Internal links are
version-relative; only the version `<select>` is absolute (`/js_of_ocaml`). The
themed stylesheet is served centrally at `/css/ocsigen-odoc.css` by ocsigen.org.

### Preview locally

The pages reference the shared site assets (`/css/…`, `/img/…`) by absolute path,
so a bare build is unstyled offline. Add `--local` to also fetch them next to the
output, then serve it:

```
wodoc build --config doc/wodoc --label dev --out _doc-site/dev \
  --menu https://ocsigen.org/doc/menu.html --local
cd _doc-site && python3 -m http.server   # open http://localhost:8000/dev/js_of_ocaml/
```

## Deployment (CI)

[`.github/workflows/doc.yml`](../.github/workflows/doc.yml) builds and publishes
to the project's **`gh-pages`** branch (served at `ocsigen.org/js_of_ocaml/`). The
CI triggers **only on `master`**, so pushing a branch never deploys:

- **push to `master`** → rebuilds and deploys the **`dev`** docs only (`dev/`).

Stable versions (e.g. `6.3.1`, built from the release tag) are **not** built by
CI: they are generated by hand and committed to `gh-pages`, with the `latest`
symlink repointed, at release time. Each CI run replaces only the `dev/`
directory; the other version directories already on `gh-pages` are preserved.
The `js_of_ocaml.yml` CI also runs `dune build @doc @doc-manual` on PRs as a
compile check.

## Releasing a stable version

js_of_ocaml's `master` diverges substantially from the latest release, so a
stable version is **not** a frozen copy of `dev/` (that would mislabel master's
API). Run the **Documentation** workflow manually (GitHub → Actions →
*Documentation* → "Run workflow") with the **version** input set to the release
tag (e.g. `6.3.1`). The `release` job checks out that tag, overlays the current
`doc/` config + manual, builds the docs labelled `<version>`, and publishes them
as `/<version>/`, repointing `latest` and refreshing `versions.json` (it stages
the build then runs `wodoc release --from <staging> --version <version>`).
No `dev/` freeze is used for js_of_ocaml.
