Useful Features of MoonBit's moon CLI - Built-in lint, bench, snapshot, coverage, doc

Published: (December 23, 2025 at 12:36 PM EST)
6 min read
Source: Dev.to

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:

  1. The directory contains a moon.mod.json.
  2. Its moon.pkg.json has "is-main": true.
  3. One of the .mbt files defines fn 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:

LocationSyntaxHow it’s executed
.mbt.md filesmbt test code fencesTreated 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

CommandWhat it does
moon checkType‑checking + linting (warnings are strict).
moon run --targetExecute a program on a specific backend.
moon test --targetRun tests on a specific backend.
moon test -uUpdate snapshot tests (inspect).
moon coverage analyzeProduce a test‑coverage report.
moon docList public methods and signatures for a type.
moon benchRun benchmarks.
moon ideSearch 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.

Back to Blog

Related posts

Read more »

Cinematic ANSI Banners for Rust

tui-banner: Instantly Turn Your Rust CLI into a Cinematic Masterpiece! !Banner previewhttps://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%...