Useful Features of MoonBit's moon CLI - Built-in lint, bench, snapshot, coverage, doc
Source: Dev.to
TL;DR
- Type checking and linting with
moon lint- Target‑specific execution with
moon run- Doc tests and snapshot tests with
moon test- Type definition generation with
moon info- Searching built‑in types and methods with
moon doc- Benchmarking with
moon bench- Coverage analysis with
moon coverage analyze- Direct LSP symbol search with
moon ide
Type Checking and Warnings with moon check
In MoonBit there is no separate “lint” step—type checking and linting are integrated, and warnings are quite strict by default. For example, the linter catches unused variables and unused type parameters.
❯ moon check
Warning: [0053]
╭─[ /Users/mizchi/mizchi/luna.mbt/src/astra/tree/builder.mbt:20:12 ]
│
20 │ pub fn[F : @env.FileSystem] build_document_tree_with_fs(
│ ───────┬───────
│ ╰───────── Warning (unused_trait_bound): This trait bound is unused.
────╯
Finished. moon: ran 419 tasks, now up to date (1 warnings, 0 errors)
Treat Warnings as Errors
Add --deny-warn to make warnings fail the command (exit code 1). This is handy for CI pipelines.
❯ moon check --deny-warn
Failed with 0 warnings, 1 errors.
error: failed when checking project
Suppressing Specific Warnings
You can silence warnings in moon.mod.json or moon.pkg.json with the warn-list field.
{
"warn-list": "-unused_trait_bound"
}
- A leading
-removes a warning. - A leading
+adds a warning that isn’t enabled by default.
Get the full list of warning IDs with:
moonc build-package -warn-help
(Historically “warn” and “alert” were separate concepts; they have since been unified.)
Specifying a Target with moon run --target
You can run a MoonBit program with moon run when the following conditions are met:
- The directory contains a
moon.mod.json. - Its
moon.pkg.jsonhas"is-main": true. - One of the
.mbtfiles definesfn main {}.
Typical invocations:
moon run main.mbt
moon run .
moon run cmd/main.mbt
Choose the Build Target
Use --target to pick a compilation target. The special target all runs the program on every supported backend (except the experimental LLVM backend).
moon run main.mbt --target wasm-gc # WebAssembly (GC)
moon run main.mbt --target js # JavaScript
moon run main.mbt --target native # Native binary
moon run main.mbt --target all # All supported targets
Testing with moon test
moon test works like moon run but executes test suites. You can also specify a target, and --target all is available.
Minimal Test Example
test {
assert_true(true)
}
Run it on a specific backend:
moon test --target js
moon test --target native
moon test --target all
Documentation Tests
MoonBit supports two kinds of doc tests:
| Location | Syntax | How it’s executed |
|---|---|---|
.mbt.md files | mbt test code fences | Treated as a normal test { … } block |
| Inline comments | `/// | …` |
Example (inline comment style):
///|
/// Increment an integer by 1
///
/// `mbt test
/// inspect(incr(41), content="42")
/// `
pub fn incr(x: Int) -> Int {
x + 1
}
Because documentation and tests are integrated, sample code can never become stale.
Built‑in Snapshot Tests with moon test -u
The inspect function together with moon test -u makes snapshot testing a breeze.
test "snapshot" {
inspect([1, 2, 3], content="")
}
Running the test with the update flag fills in the expected snapshot automatically:
moon test -u
Result:
test "snapshot" {
inspect([1, 2, 3], content="[1, 2, 3]")
}
A common workflow is to start with content="", run moon test -u, review the generated snapshot, and commit the updated test.
Coverage Analysis with moon coverage analyze
You can generate a coverage report that highlights code not exercised by your test suite.
moon coverage analyze
(Add a concrete example here later.)
Searching Built‑in Types and Methods with moon doc
moon doc prints the public API of a type, letting you explore method signatures without digging into the standard‑library source.
moon doc StringView # Show methods for StringView
moon doc Array # Show methods for Array
moon doc Map # Show methods for Map
Example Output for StringView
❯ moon doc StringView
package "moonbitlang/core/string"
type StringView
pub fn StringView::add(Self, Self) -> Self
pub fn StringView::at(Self, Int) -> UInt16
pub fn StringView::char_length(Self) -> Int
pub fn StringView::char_length_eq(Self, Int) -> Bool
pub fn StringView::char_length_ge(Self, Int) -> Bool
pub fn StringView::code_unit_at(Self, Int) -> UInt16
pub fn StringView::compare(Self, Self) -> Int
pub fn StringView::contains(Self, Self) -> Bool
pub fn StringView::contains_any(Self, chars~ : Self) -> Bool
pub fn StringView::contains_char(Self, Char) -> Bool
pub fn StringView::data(Self) -> String
pub fn StringView::default() -> Self
pub fn StringView::equal(Self, Self) -> Bool
pub fn StringView::find(Self, Self) -> Int?
pub fn StringView::find_by(Self, (Char) -> Bool) -> Int?
...
StringView is a zero‑copy, immutable view into a slice of a String. It provides an API similar to String while avoiding the cost of copying large strings—something you won’t find in many other languages.
Quick Recap
| Command | What it does |
|---|---|
moon check | Type‑checking + linting (warnings are strict). |
moon run --target | Execute a program on a specific backend. |
moon test --target | Run tests on a specific backend. |
moon test -u | Update snapshot tests (inspect). |
moon coverage analyze | Produce a test‑coverage report. |
moon doc | List public methods and signatures for a type. |
moon bench | Run benchmarks. |
moon ide | Search symbols via the built‑in LSP. |
These commands give you a powerful, all‑in‑one workflow even when the ecosystem’s library selection is still growing. Happy MoonBiting!
Benchmarking with moon bench
You can run benchmarks with moon bench.
Example usage
test "fib benchmark" (b : @bench.Test) {
b.bench(fn() { fib(20) })
}
A test block that receives a b : @bench.Test parameter is recognized as a benchmark.
Calculations without side effects might be optimized away by the compiler, so use b.keep() to retain the result.
test "sum benchmark" (b : @bench.Test) {
let result = b.bench(fn() {
let mut sum = 0
for i = 0; i
})
}
This was very helpful in my markdown compiler implementation.
Using IDE Symbol Search Directly with moon ide
Amazingly, you can directly use LSP features like symbol search and go‑to‑definition from the command line. I learned about this from the author’s demo at an online meetup. There’s no official documentation yet, but the commands exist.
Find References
❯ moon ide find-references -query "normalize_path"
fn normalize_path in package mizchi/luna/sol/router at /Users/mizchi/mizchi/luna.mbt/src/sol/router/sol_routes.mbt:270-297:
^^^^^^^^^^^^^^
Found 9 references of this symbol:
/Users/mizchi/mizchi/luna.mbt/src/sol/router/sol_routes.mbt:272:4-272:18:
|
| ///|
| /// Normalize path (remove duplicate slashes, trailing slash)
272 | fn normalize_path(path : String) -> String {
| ^^^^^^^^^^^^^^
| if path == "" || path == "/" {
| return "/"
Go to Definition
$ moon ide goto-definition -query="normalize_path"
There are total 2 symbols match query 'normalize_path':
Top 2 best‑matching symbols:
fn normalize_path in package mizchi/luna/sol/router at /Users/mizchi/mizchi/luna.mbt/src/sol/router/sol_routes.mbt:270-297:
^^^^^^^^^^^^^^
270 | ///|
| /// Normalize path (remove duplicate slashes, trailing slash)
| fn normalize_path(path : String) {
| if path == "" || path == "/" {
| return "/"
| }
| let chars = path.to_array()
| let result : Array[Char] = []
| let mut prev_slash = false
There also seems to be a moon ide peek-def command, but I couldn’t figure out how to use it since there’s no documentation. It appears to be used like:
moon ide peek-def -loc ./src/luna/routes/compile.mbt:6:4 -symbol normalize_path
Conclusion
What do you think? Similar features exist in TypeScript’s ESLint and Vitest, or Cargo’s Criterion, but I don’t know of any other language with such rich functionality built into the language’s CLI. This abundance of features helps you get by even when libraries are lacking.
It’s also interesting that moon doc and moon ide seem to be designed with AI in mind.
Of course, since it’s a young language, you’ll encounter bugs when using it—especially with specific backend + feature combinations. If you find any, please report them here. I’ve reported about five issues on the repo and about seven directly on Discord.
In my experience, critical issues usually get fixed by the following Tuesday.