$ opam install melange
Dune can build projects using Melange, and it allows the user to produce
used with Melange by adding
(modes ...) in the
Melange support is still experimental in Dune and needs to be enabled in the dune-project file:
(using melange 0.1)
Once that’s in place, you can use the Melange mode in library stanzas
Let’s start by looking at a simple project with Melange and Dune. Subsequent sections explain the different concepts used here in further detail.
First, make sure that the dune-project file specifies at least version 3.8 of the dune language and the Melange extension is enabled:
(lang dune 3.11) (using melange 0.1)
(melange.emit (target output))
Finally, add a source file to build:
echo 'Js.log "hello from melange"' > hello.ml
dune build @melange or just
dune build, Dune
produces the following file structure:
. ├── _build │ └── default │ └── output │ └── hello.js ├── dune ├── dune-project └── hello.ml
$ node _build/default/output/hello.js hello from melange
Adding Melange support to Dune libraries is done as follows:
(modes melange): adding
modesis required. This field also supports the Ordered Set Language.
(melange.runtime_deps <deps>): optionally, define any runtime dependencies using
melange.runtime_deps. This field is analog to the
runtime_depsfield used in
New in version 3.8.
from Melange libraries and entry-point modules. It’s similar to the OCaml
executable stanza, with the exception that there is no linking step.
(melange.emit (target <target>) <optional-fields>)
be placed. In particular, the folder will be placed under
The result of building a
melange.emit stanza will match the file structure
of the source tree. For example, given the following source tree:
├── dune # (melange.emit (target output) (libraries lib)) ├── app.ml └── lib ├── dune # (library (name lib) (modes melange)) └── helper.ml
The resulting layout in
_build/default/output will be as follows:
output ├── app.js └── lib ├── lib.js └── helper.js
(alias <alias-name>)specifies an alias to which to attach the targets of the
These targets include the
.jsfiles generated by the stanza modules, the targets for the
.jsfiles of any library that the stanza depends on, and any copy rules for runtime dependencies (see
By default, all stanzas will have their targets attached to an alias
melange. The behavior of this default alias is exclusive: if an alias is explicitly defined in the stanza, the targets from this stanza will be excluded from the
The targets of
melange.emitare also attached to the Dune default alias (
@all), regardless of whether the
(alias ...)field is present.
commonjswill follow CommonJS modules, and will produce require calls and export values with
.js. You can specify a different extension with a pair
(<module_system> <extension>), e.g.
(module_systems (es6 mjs)).
Multiple module systems can be used in the same field as long as their extensions are different. For example,
.jsextension, and another using ES6 and the
(modules <modules>)specifies what modules will be built with Melange. By default, if this field is not defined, Dune will use all the
.ml/.refiles in the same directory as the
dunefile. This includes module sources present in the file system as well as modules generated by user rules. You can restrict this list by using an explicit
<modules>uses the Ordered Set Language, where elements are module names and don’t need to start with an uppercase letter. For instance, to exclude module
(modules :standard \ foo).
(libraries <library-dependencies>)specifies Melange library dependencies. Melange libraries can only use the simple form, like
(libraries foo pkg.bar). Keep in mind the following limitations:
re_exportform is not supported.
All the libraries included in
<library-dependencies>have to support the
melangemode (see the section about libraries below).
melange.emitstanza will belong.
(runtime_deps <paths-to-deps>)specifies dependencies that should be copied to the build folder together with the
runtime_depsadhere to the formats in Dependency Specification. For example
(runtime_deps ./path/to/file.css (glob_files_rec ./fonts/*)).
(emit_stdlib <bool>)allows the user to specify whether the Melange standard library should be included as a dependency of the stanza or not. The default is
true. If this option is
(promote <options>)promotes the generated
.jsfiles to the source tree. The options are the same as for the rule promote mode. Adding
(promote (until-clean))to a
melange.emitstanza will cause Dune to copy the
.jsfiles to the source tree and
dune cleanto delete them.
(preprocess <preprocess-spec>)specifies how to preprocess files when needed. The default is
no_preprocessing. Additional options are described in the Preprocessing Specification section.
(preprocessor_deps (<deps-conf list>))specifies extra preprocessor dependencies, e.g., if the preprocessor reads a generated file. The dependency specification is described in the Dependency Specification section.
(compile_flags <flags>)specifies compilation flags specific to
melc, the main Melange executable.
<flags>is described in detail in the Ordered Set Language section. It also supports
(:include ...)forms. The value for this field can also be taken from
envstanzas. It’s therefore recommended to add flags with e.g.
(compile_flags :standard <my options>)rather than replace them.
(root_module <module>)specifies a
root_modulethat collects all listed dependencies in
libraries. See the documentation for
root_modulein the library stanza.
(allow_overlapping_dependencies)is the same as the corresponding field of library.
Keep Bundles Small by Reducing the Number of
It is recommended to minimize the number of
that a project defines: using multiple
melange.emit stanzas will cause
are used across them. As an example:
(melange.emit (target app1) (libraries foo)) (melange.emit (target app2) (libraries foo))
foo will be emitted twice in the
_build folder. They will be present under
This can have unexpected impact on bundle size when using tools like Webpack or
Esbuild, as these tools will not be able to see shared library code as such,
as it would be replicated across the paths of the different stanzas
Faster Builds With
Melange libraries might be installed from the
npm package repository,
unnecessary folders in
node_modules, it is recommended to explicitly
include only the folders that are relevant for Melange builds.
This can be accomplished by combining subdir and dirs
stanzas in a
dune file next to the
node_modules folder. The
vendored_dirs stanza can be used to avoid warnings in Melange
libraries during the application build. The data_only_dirs stanza
can be useful as well if you need to override the build rules in one of the
(subdir node_modules (vendored_dirs reason-react) (dirs reason-react))