package mdx

  1. Overview
  2. Docs
Executable code blocks inside markdown files

Install

Dune Dependency

Authors

Maintainers

Sources

mdx-1.2.0.tbz
sha256=bf01c0280f6953d63e1a6bd442cc1bbf4670eb463b049494ece08abe2c5de3ba
md5=c8237ce970b3e891d608bf061350552c

Description

mdx allows to execute code blocks inside markdown files. There are (currently) two sub-commands, corresponding to two modes of operations: pre-processing (mdx pp) and tests (mdx test).

The pre-processor mode allows to mix documentation and code, and to practice "literate programming" using markdown and OCaml.

The test mode allows to ensure that shell scripts and OCaml fragments in the documentation always stays up-to-date.

mdx is released as a single binary (called mdx).

Published: 10 Jan 2019

README

README.md

mdx -- executable code blocks inside markdown files

mdx allows to execute code blocks inside markdown files. There are (currently) two sub-commands, corresponding to two modes of operations: pre-processing (mdx pp) and tests (mdx test).

The pre-processor mode allows to mix documentation and code, and to practice "literate programming" using markdown and OCaml.

The test mode allows to ensure that shell scripts and OCaml fragments in the documentation always stays up-to-date.

mdx is released as a single binary (called mdx) and can be installed using opam:

$ opam install mdx

Supported Extensions

Shell Scripts

mdx interprets shell scripts inside sh code blocks as cram-like tests. The syntax is the following:

  • Lines beginning with a dollar sign and a space are commands and will be run in the shell.

  • Multi-lines commands end by \ and continue with two spaces and a > sign on the next line:

     ```sh
     $ <line1> \
     > <line2> \
     > <line3>
     ```
    
  • Commands support the heredoc syntax (<<):

     ```sh
     $ cat <<EOF \
     > hello\
     > world\
     > EOF
     hello
     world
     ```
    
  • Lines beginning without a dollar sign are considered command outputs.

  • Command outputs can contains ellipsis: .... These will match any possible outputs (on zero, one or multiple lines).

  • Arbitrary padding with whitespace is supported, as long as it is consistent inside a code block.

Here is an example of a markdown file using shell scripts inside code blocks, with a padding of 3:

```sh
   $ for i in `seq 1 10`
   1
   ...
   10
```

mdx will also consider exit codes when the syntax [<exit code>]is used:

```sh
$ exit 1
[1]
```

Note that nothing will be displayed when the exit code is 0 (e.g. in case of success).

OCaml Code

mdx interprets OCaml fragments. It understands normal code fragments and toplevel code fragments (starting with a # sign and optionally ending by ;;). Arbitrary whitespace padding is supported, at long as it stays consistent within a code block.

Toplevel fragments interleaves OCaml code and their corresponding outputs.

Here is an example of normal OCaml code:

```ocaml
print_endline "42"
```

Here is an examples of toplevel OCaml code:

```ocaml
# print_endline "42"
42
```

Pre-processing

mdx pp allows to transform a markdown file into a valid OCaml file, which can be passed to OCaml using the -pp option.

For instance, given the following file.md document:

```ocaml
# print_endline "42"
42
```

Can be compiled and executed using:

$ ocamlc -pp 'mdx pp' -impl file.md -o file.exe
$ ./file.exe
42

This can be automated using dune:

(rule
 ((targets (file.ml))
  (deps    (file.md))
  (action  (with-stdout-to ${@} (run mdx pp ${<})))))

(executable ((name file)))

Tests

Cram Tests

Cram tests can be executed and checked with mdx test <file.md>.

```sh
 $ for i in `seq 1 10`; do echo $i; done
 1
 ...
 10
 ```

If the output is not consistent with what is expected, <file.md>.corrected is generated.

OCaml

To execute OCaml code and toplevel fragments, uses mdx test <file.md>.

```ocaml
# print_endline "42"
42
```

If the output is not consistent with what is expected <file.md>.corrected is generated.

Integration with Dune

To test that the code blocks of file.md stays consistent, one can use dune's diff? stanza:

(alias
 ((name runtest)
  (deps (file.md))
  (action (progn
           (run mdx test ${<})
           (diff? ${<} ${<}.corrected)))))

This allows to test the consistency of a markdown file using the normal dev workflow:

$ dune runtest

will display a diff of the output if something has changed. For instance:

$ dune runtest
------ file.md
++++++ file.md.corrected
File "file.md", line 23, characters 0-1:
 |
 |```sh
-| $ for i in `seq 1 3`; do echo $i; done
+| $ for i in `seq 1 4`; do echo $i; done
 | 1
 | 2
 | 3
+| 4
 |```

And the changes can then be accepted using:

$ dune promote
Non-deterministic Tests

Non-deterministic Outputs

mdx test supports non-deterministic outputs:

```sh non-deterministic=output
$ <command>
<output>
```

In that case, ppx test <file> will run the command but will not generate <file>.corrected if the new output differs from the one described in the file. Use mdx test --non-deterministic <file> to come back to the default behaviour.

Non-deterministic Commands

mdx test supports non-deterministic commands:

```ocaml non-deterministic=command
# Random.int 10;;
- : int = 5
```

In that case, mdx test <file> will not run the command. Use mdx test --non-deterministic <file> to come back to the default behaviour.

Sections

It is possible to test or execute only a subset of the file using sections using the --section option (short name is -s). For instance mdx pp -s foo will only consider the section matching the perl regular expression foo.

Dependencies (11)

  1. ocaml-migrate-parsetree >= "1.0.6" & < "2.0.0"
  2. ocamlfind >= "1.7.2"
  3. result
  4. re >= "1.7.2"
  5. cmdliner >= "1.0.0"
  6. logs
  7. astring
  8. cppo build & >= "1.1.0"
  9. fmt >= "0.8.5"
  10. dune
  11. ocaml >= "4.02.3" & < "4.08.0"

Dev Dependencies (3)

  1. cmdliner with-test & < "1.1.0"
  2. conf-pandoc with-test
  3. lwt with-test

Used by (53)

  1. calculon = "0.4"
  2. calculon-web = "0.4"
  3. carbon
  4. castore >= "0.0.2"
  5. cconv-ppx
  6. cohttp-eio
  7. containers >= "2.6" & < "2.7"
  8. containers-data >= "3.11"
  9. current_examples >= "0.6"
  10. current_git >= "0.6.1"
  11. dolmen >= "0.8" & < "0.10"
  12. dolmen_loop = "0.9"
  13. electrod >= "0.5"
  14. ezcurl-lwt
  15. fuseau
  16. geojson
  17. geojsone
  18. gitlab < "0.1.1"
  19. gitlab-jsoo < "0.1.1"
  20. gitlab-unix < "0.1.1"
  21. hilite
  22. http-cookie >= "4.1.0"
  23. ISO3166
  24. iter < "1.3"
  25. lab < "0.1.8"
  26. lwt-pipe
  27. msat >= "0.8"
  28. multibase
  29. multicodec
  30. multihash-digestif
  31. odoc >= "2.0.0" & < "2.3.0"
  32. ortac-qcheck-stm < "0.2.0"
  33. pa_ppx_parsetree < "0.02"
  34. pa_ppx_quotation2extension < "0.02"
  35. pa_ppx_regexp < "0.02"
  36. pa_ppx_static < "0.02"
  37. polars
  38. polars_async
  39. ppx_deriving_yaml < "0.2.2"
  40. preface
  41. printbox = "0.2"
  42. reparse >= "3.0.0"
  43. routes != "0.7.2"
  44. search
  45. smtml >= "0.3.1"
  46. spelll >= "0.3"
  47. spin = "0.6.0"
  48. tls-eio
  49. wtr
  50. wtr-ppx
  51. yaml >= "1.0.0" & < "3.1.0"
  52. yaml-sexp < "3.1.0"
  53. zarith-ppx

Conflicts

None

OCaml

Innovation. Community. Security.