Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions .github/workflows/doc.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Build the TyXML documentation (manual + API) with odoc, theme it with the
# Ocsigen chrome (wodoc), and publish it on the project's gh-pages branch, served
# at https://ocsigen.org/tyxml/. See doc/README.md for the full picture.
#
# CI deploys ONLY the "dev" docs and ONLY from master. Stable versions are built
# by hand and committed to gh-pages at release time, so there is no dispatch /
# release path here. Each run replaces only the dev/ directory; the other version
# directories already on gh-pages are PRESERVED.
name: Documentation

on:
push:
branches: [master]
workflow_dispatch: {}

jobs:
build-deploy:
if: github.repository == 'ocsigen/tyxml'
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Set up OCaml
uses: ocaml/setup-ocaml@v3
with:
ocaml-compiler: "5.4.0"

- name: Install dependencies
run: |
opam install . --deps-only --with-doc # odoc is a with-doc dep
# wodoc: the odoc driver that themes the pages with the Ocsigen chrome.
opam pin add -n wodoc https://github.com/ocsigen/wodoc.git
opam install wodoc

- name: Build documentation (dev)
# wodoc build runs `dune build @doc --profile release`, assembles the
# themed site from doc/wodoc, and fetches the shared menu from its single
# canonical copy in ocsigen.github.io.
run: >-
opam exec -- wodoc build --config doc/wodoc
--menu https://ocsigen.org/wodoc/menu.html
--out "$PWD/_doc-site/dev" --label dev

- name: Deploy to gh-pages (replace only dev/, keep the rest)
uses: JamesIves/github-pages-deploy-action@v4
with:
branch: gh-pages
folder: _doc-site/dev
target-folder: dev
clean: true
24 changes: 2 additions & 22 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -21,26 +21,6 @@ clean:
.PHONY: doc
doc:
dune build @doc

# WIKIDOC stuff
# Should have wikidoc installed with
# opam pin add https://github.com/ocsigen/wikidoc.git

DOCDIR=_wikidoc

$(DOCDIR)/.git:
mkdir -p $(DOCDIR)
cd $(DOCDIR) && (\
git clone -b wikidoc git@github.com:ocsigen/tyxml.git . \
)

.PHONY: doc wikidoc
wikidoc: build $(DOCDIR)/.git
make -C docs wikidoc; exit 0
rm -rf _wikidoc/docs/dev/*
cp -r docs/api/wiki _wikidoc/docs/dev/api/
cp -r docs/manual-wiki _wikidoc/docs/dev/manual
git -C $(DOCDIR) add --all
git -C $(DOCDIR) commit -a -m "wikidoc updates"
git -C $(DOCDIR) push origin wikidoc
# The themed documentation site is built by wodoc from doc/wodoc; see
# doc/README.md. (The legacy ocamldoc/wikidoc targets have been removed.)

54 changes: 54 additions & 0 deletions doc/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# How the TyXML documentation is generated

The TyXML documentation published at <https://ocsigen.org/tyxml/> is built 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 | [`docs/*.mld`](../docs) (intro, functors, ppx, jsx) | odoc pages |
| API overview | [`docs/api.mld`](../docs/api.mld) | odoc page |
| API | the `.mli` of the `tyxml` package | odoc comments |
| Site configuration (nav, …) | [`doc/wodoc`](wodoc) | wodoc config (S-expression) |

TyXML has **no client/server split**, so a single `dune build @doc` (plain odoc)
builds the manual and the API together — no odoc-driver. The auxiliary packages
(`tyxml-ppx`/`-syntax`/`-jsx`) live on ocaml.org; the manual's ppx/jsx pages
document their use. The left navigation and page theming are declared in
[`doc/wodoc`](wodoc) and produced by `wodoc build`.

## Build

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

`wodoc build` runs `dune build @doc --profile release` (the dev profile treats
warning 67 as an error), assembles every page into the Ocsigen site (shared
header/menu/drawer, the version `<select>`, the left navigation from `doc/wodoc`),
and writes the version redirect. `--menu` is fetched from its single canonical
copy in `ocsigen.github.io`.

### Preview locally

Add `--local` to also fetch the shared `/css//img/` assets so the build renders
offline; `wodoc build` then prints the exact command to serve it.

## Versions and deployment (CI)

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

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

Stable versions (e.g. `4.6.0`, built from the released API at `latest-mli-odoc`)
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.
21 changes: 21 additions & 0 deletions doc/wodoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
;; Declarative wodoc config for the TyXML documentation.
;; wodoc build --config doc/wodoc --out <dir>/<label> --label <v> \
;; --menu https://ocsigen.org/wodoc/menu.html [--local]
;; (wodoc build runs `dune build @doc --profile release` itself when --src is omitted.)
;; TyXML has no client/server split, so plain `dune build @doc` is enough (no
;; odoc-driver). The auxiliary packages (tyxml-ppx/syntax/jsx) live on ocaml.org;
;; the manual's ppx/jsx pages document their use. odoc puts the manual pages and
;; the API together under the tyxml/ package dir.
(project tyxml)
(title TyXML)
(pub /tyxml)
(profile release) ; dev profile treats warning 67 as an error
(landing tyxml/intro.html) ; the manual's Introduction is the entry point
(nav
(section "TyXML - Manual"
(link "Introduction" tyxml/intro.html intro)
(link "Functorial interface" tyxml/functors.html functors)
(link "Ppx syntax extension" tyxml/ppx.html ppx)
(link "JSX syntax" tyxml/jsx.html jsx))
(section "TyXML - API"
(link "API reference" tyxml/api.html api)))
38 changes: 0 additions & 38 deletions docs/Makefile

This file was deleted.

6 changes: 2 additions & 4 deletions docs/indexdoc → docs/api.mld
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
{0 API reference}

{2 Main modules }

{!modules:
Expand Down Expand Up @@ -26,7 +28,3 @@ Xml_iter
Xml_print
Xml_stream
}

{2 Indexes}

{!indexlist}
2 changes: 2 additions & 0 deletions docs/dune
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
(documentation
(package tyxml))
114 changes: 114 additions & 0 deletions docs/functors.mld
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
{0 The functorial interface}

TyXML provides a functorial interface to implement HTML and SVG on top
of any XML implementation. This is used heavily by Eliom to implement
the [F] and [D] modules, providing respectively a functional and
a DOM version of the HTML implementation.


{1:make The [Make] functors}

These interfaces are available in the modules
{!Html_f} and {!Svg_f}.
They provide a functor [Make] which takes a concrete implementation
of Xml following {!Xml_sigs.T}. A default
implementation is provided by the {!Tyxml.Xml} module. The
functor {!Html_f.Make} also needs an Svg
implementation that one can obtain, for example, with the functor
{!Svg_f.Make}. The [Xml] always needs to provide a
module [W] of type {!Xml_wrap.T}, along with types
{@ocaml[ type 'a wrap = 'a W.t ]} and
{@ocaml[ type 'a list_wrap = 'a W.tlist ]}.
The purpose of the [Wrap] module is explained in the next section.


{1:wrap Wrapping up the nodes}

The module {!Xml_sigs.T.W} allows to wrap Xml elements
in a monad ['a t]. A good example of application is the [R]
modules with reactive nodes in Eliom. Here is the simplified signature
of the [div] element:
{@ocaml[R.div : 'a elt list t -> div elt]}
[t] will wrap the input of every [Xml] node and be
integrated in the resulting node.

The [W] module needs to implement operations over the type of nodes,
and provides a special type for lists of nodes. The [W] module
additionally provides a type [(-'a, 'b) ft] for (wrapped) functions,
whose purpose is explained in the next section. In most cases, it is
sufficient to define:
{@ocaml[ (-'a, 'b) ft = 'a -> 'b]}

An identity wrapper, {!Xml_wrap.NoWrap}, is
provided. {!Xml_wrap.NoWrap} can be used to apply the
functor without wrapping the elements.


{1:wrapped_functions The [Make_with_wrapped_functions] functors}

The [Make_with_wrapped_functions] functors (available in the modules
{!Html_f} and {!Svg_f}) differ from
the [Make] functors by requiring an additional argument [C] (of
type {!Html_f.Wrapped_functions} and
{!Svg_f.Wrapped_functions} respectively).

The [C] functor argument defines a type [(-'a, 'b) ft], and a
collection of [ft] values. For applying the functor, the type
constraint
{@ocaml[ ('a, 'b) Xml.W.ft = ('a, 'b) C.ft]}
needs to be satisfied. The [ft] values are wrapped functions that
the functor uses internally to operate on wrapped elements.

The motivation of providing the [Make_with_wrapped_functions] is as
follows. Certain monads ['a t] can only be operated upon by wrapped
functions, and not by plain OCaml functions. The wrapped functions
cannot be produced internally by TyXML, and thus have to be provided
to the functor. Our intended application is with Eliom shared (i.e.,
client-server) signals. Such signals can only be operated upon with
Eliom shared functions.

The [Make] functors simply apply the [Make_with_wrapped_functions]
functors with TyXML-provided [Wrapped_functions] modules. The latter
modules do not wrap the functions, i.e., they satisfy:
{@ocaml[(-'a, 'b) ft = 'a -> 'b]}.


{1:sig Exporting the correct signature}

In order to help export the correct signature after a functor
application, two signature functors are provided:
{!Svg_sigs.Make} and {!Html_sigs.Make}.

As an example of use, let us look at the module {!Tyxml.Svg}.
Here is the definition of the module:
{@ocaml[module M = Svg_f.Make(Tyxml_xml)]}
In this case, the declaration in the interface file should look like
this: {@ocaml[module M : Svg_sigs.Make(Tyxml_xml).T]}

The signature functor {!Svg_sigs.Make} contains only a
signature [T], which is equal to {!Svg_sigs.T}, but
exports various equalities with the module [Xml].

You should {b always} use a signature functor to give the type of a
module produced by a functor application. It will ensure that exactly
the right type equalities are exported and will naturally keep track
of changes in TyXML.

There are some important notes about these signature functors:
- [module M : Svg_sigs.Make(Tyxml_xml).T] doesn't mean that [M.Xml]
is a submodule of [Xml]. It only means that the types [uri],
[event_handler], [mouse_event_handler],
[keyboard_event_handler], [touch_event_handler],
[attrib] and [elt] are the
same in both modules. This is useful when not exporting the exact
module that was used in the functor, but another (smaller and
simpler) module. This is the case in {!Js_of_ocaml_tyxml.Tyxml_js.R},
for example.

- {!Html_f} and {!Svg_f} functors
export two additional equalities, [+'a elt = Xml.elt] and
[+'a attrib = Xml.attrib]. These equalities {b should never be
exported in a public interface}. Exporting them would break HTML
typing by allowing to build invalid HTML trees. These equalities
are useful internally, for example in Eliom they are used to make
[F.elt], [D.elt] and [R.elt] equals.
Loading
Loading