Contributing
November 30, 2025 ยท View on GitHub
Thanks for your interest! Below we describe Melange development setup for a few different package managers. If something isn't working, or you'd like to add another workflow, please let us know by filing an issue.
Installation
Melange can be set up with Nix or opam. Instructions for each of them are detailed below.
Nix
Prerequisites:
- Install the Nix package manager
- Enable Nix Flakes
The best way to get started is to get a nix shell running:
# Runs a shell in the nix environment.
$ make nix-zsh
Once you're in the shell, you have access to dune, node, yarn, and all the other executables you need to get down to business.
- You can also use
make nix-*to execute arbitrary commands in the nix environment. e.g. If you need a text editor running with the nix environment you can do
make nix-nvim # Runs nvim in the nix environment
make nix-code # Opens VSCode in the nix environment
make nix-fish # Runs fish shell in the nix environment
# etc.
OPAM
Prerequisites:
- Install the opam package manager
- Install
treecommand line tool (brew install treefor macOS orapt install treefor Linux)
After cloning the repository, make sure to initialize git submodules, so that
melange-compiler-libs is updated:
$ git submodule update --init --recursive --remote
To set up a development environment using opam, run make opam-init to set up an opam local switch and download the required dependencies.
Install the dependencies in melange.opam without building and installing melange itself by running:
$ opam install ./melange.opam --deps-only
If you plan to work on improving documentation, you will need to install odoc: opam install odoc.
Developing
Here are some common commands you may find useful:
dune buildbuilds the whole projectdune runtestruns the native testsdune exec jscomp/main/melc.exeis useful to run the development version ofmelc
Updating the vendor/melange-compiler-libs submodule:
- Make your change in
vendor/melange-compiler-libs - Commit to your fork of the
melange-compiler-libsrepo and get a PR through - Commit the updated branch to the submodule in this repository
- To make it build in CI, change the
melange-compiler-libsinput URL in flake.nix (if necessary, e.g. to point to an unmerged branch), then runnix flake updateand commit the modifiedflake.lock
Submitting a Pull Request
When you are almost ready to open a PR, it's a good idea to run the full CI test suite locally to make sure everything works. First, open a new non-nix shell or exit your current nix shell by using the exit command or CTRL+D. Then, run the following command:
# Runs the full CI test suite
$ nix-build nix/ci/test.nix
If that all passes, then congratulations! You are well on your way to becoming a contributor ๐
To submit a Pull Request, follow this guide on creating one from a fork.
Releasing Melange
Releasing Melange involves a few steps across different package managers and GitHub repos:
- Melange releases across a few OCaml versions to the OPAM repository. To
release to OPAM:
- cut release branches e.g. v5-414, v5-51, v5-52, v5-53
- These branches also serve as long-term release maintenance branches if new patch releases need to be cut.
- for each new branch, check out the corresponding
vendor/melange-compiler-libsbranch in the submodule (e.g. 4.14, 5.1, 5.2) - use dune-release
to release the Melange package, adding
-p melangeand--include-submodulesin the relevant commands
- cut release branches e.g. v5-414, v5-51, v5-52, v5-53
- Since the resolution of
melange-re/melange#620,
Melange releases also include publishing the Melange runtime / stdlib to NPM
in their compiled form. This makes it easier to use Melange and
(emit_stdlib false)with a well-known, already compiled runtime, speeding up builds in a lot of cases. To release the Melange compiled runtime / stdlib to NPM:- set the
MELANGE_RUNTIME_VERSIONvariable in./runtime-export/dune - run
dune build @runtime, preferably in the tagged release branch. cdinto each of the newly creatednode_modules/{melange.js,melange,melange.belt}and runnpm publish, in this order.
- set the
- Finally, follow the release process documented in melange-re/melange-re.github.io to publish the Melange website for the new version at https://melange.re.
Update JS Reserved Keywords Map
The compiler sources include a list of reserved JS keywords in
jscomp/ext/js_reserved_map.ml which includes all identifiers in global scope
(window / globalThis). This list should be updated from time to time for
newer browser versions.
To update it, run:
npm install puppeteer
node scripts/build_reserved.js
Since melange-re/melange#1665, this is now done periodically and automatically. The workflow may also be triggered manually.
Upgrading the Flow JS parser
Melange vendors a copy of Facebook's Flow parser. It's used, among other
things, to parse the JS code under %mel.raw extension points. From time to
time we want to upgrade the Flow parser to get the newer features added to
JavaScript.
Follow these steps to upgrade the vendored Flow parser within Melange:
-
Clone the Flow repository and build the parser with
dune b -p flow_parser -
Copy the
.ml{,i}sources tojscomp/js_parser:
$ cp \
${FLOW_SRC}/src/parser/*.ml{,i} \
${FLOW_SRC}/src/hack_forked/utils/collections/third-party/flow_{set,map}.ml \
${FLOW_SRC}/src/third-party/sedlex/flow_sedlexing.ml{,i} \
${MELANGE_SRC}/jscomp/js_parser
- Write a small program to get the expanded AST back to source code
- this way we don't need to depend on the sedlex PPX in Melange. We'll be copying the expanded sources from the dune build folder
For .ml files:
(* (executable (name x) (libraries compiler-libs.common)) *)
let () =
let ast = Pparse.read_ast Structure Sys.argv.(1) in
Format.printf "%a" Pprintast.structure ast
For .mli files:
(* (executable (name x) (libraries compiler-libs.common)) *)
let () =
let ast = Pparse.read_ast Signature Sys.argv.(1) in
Format.printf "%a" Pprintast.signature ast
- Run this small program on the following modules:
Flow_lexer,Parse_error,Enum_commonandToken:
# Example for `Flow_lexer`
dune exec ./pp_ast.exe -- flow/_build/default/src/parser/flow_lexer.pp.ml > jscomp/js_parser/flow_lexer.ml
dune exec ./pp_ast.exe -- flow/_build/default/src/parser/flow_lexer.pp.mli > jscomp/js_parser/flow_lexer.mli
- Prune unnecessary modules (e.g. look at
${FLOW_SRC}/src/parser/duneand remove the modules that aren't part of the flow_parser library, etc). A good rule of thumb is to prune most new files after staging them in Git. If they aren't used in any other modules, most likely Melange won't either.
A whirlwind tour to the compiler codebase
Folder structure:
bincontains all binaries shipped in a Melange distribution.docscontains the old BuckleScript manual which we host to consult (rarely).jscompis where the compiler implementation and some tests live:commondefines themelange_ffiprivate library, housing code shared by both the Melange core library andmelange.ppx.coredefines thecoreMelange library, containing the bulk of the compiler backend implementation.melstdcontains the sources for theextprivate library. This is a standard library extension with additional functions and data structures used throughout the Melange code.js_parseris a vendored copy of the Flow parser, used in Melange to classify%mel.rawJavaScript code.runtimeis the Melange runtime. It has:- the
melange.jslibrary. - the
Caml_*modules, implementing the low-level OCaml primitives. While these are technically part of themelange.jslibrary, they:- aren't exposed in the
Js.*module. - are only ever accessed from JS: Melange internals don't address any of these modules directly, and you shouldn't either.
- are still exposed e.g. as
Js__Caml_arraybecause of Dune namespacing implementation details.
- aren't exposed in the
- the
stdlibis the Melange stdlib, included in the final distribution.- Depends on the runtime
otherscontains the sources for the additional opt-in libraries distributed with Melange:melange.belt,melange.domandmelange.node.testhas both:- the sources for the Melange runtime tests that are executed on every CI run
- a snapshot of their compilation to JavaScript (in
jscomp/test/distandjscomp/test/dist-es6)- NOTE: these snapshots are currently built manually. To do so,
comment the only line in
jscomp/duneand rundune build.
- NOTE: these snapshots are currently built manually. To do so,
comment the only line in
playgroundcontains the Melange in-browser playground codeppxhas the code formelange.ppx, for example:- all the
%mel.*extensions and@derivingderivers are declared inppx/melange_ppx.ml. - the Melange FFI (Foreign Function Interface)
externals and attributes are interpreted in the PPX. - the Melange PPX isn't just a traditional syntax transformation: some attributes and functions generated by the PPX get interpreted later during Melange compilation. This piece of code shows an example of that.
- all the
rfcshas the Melange RFC template and the RFCs to Melange that have been accepted to the project.testis where the Melange unit tests and the blackbox cram tests are located.vendorincludes themelange-compiler-libsGit submodule.