Package Configuration#

moon uses a package file to identify and describe a package. The legacy format is moon.pkg.json, and the new format is moon.pkg. For full JSON schema, please check moon's repository.

New format (moon.pkg)#

The new format is a concise DSL. You can generate or reformat it from an existing moon.pkg.json with:

moon fmt -C <module_dir>

Example:

import {
  "moonbit-community/language/packages/virtual",
}

options(
  "is-main": true,
  overrides: [ "moonbit-community/language/packages/implement" ],
)

In moon.pkg, dependencies are declared in an import { ... } block. Use @alias to set a custom alias:

import {
  "moonbit-community/language/packages/pkgA",
  "moonbit-community/language/packages/pkgC" @c,
  "moonbitlang/core/builtin",
}

All other fields from moon.pkg.json move into a single options(...) block. The key names and value shapes are unchanged; the legacy keys that contain - must be quoted.

options(
  "virtual": { "has-default": true },
)

The moon.pkg format allows comments //....

Full syntax of moon.pkg is as follows:

moon_pkg ::= statement*
statement ::= import | assign | apply

import ::= "import" "{" (import_item ",")* import_item? "}" import_kind?
import_item ::= STRING ("@" PKG_NAME)?
import_kind ::= "for" STRING

assign ::= LIDENT "=" expr

apply ::= LIDENT "(" (argument ",")* argument? ")"
argument ::= LIDENT ":" expr | STRING ":" expr  

expr ::= array | object | apply | STRING | INT | "true" | "false"
array ::= "[" (expr ",")* expr? "]"
object ::= "{" (field ",")* field? "}"

Name#

The package name is not configurable; it is determined by the directory name of the package.

Formatter#

The formatter field configures moon fmt for this package. Currently it supports ignore, a list of file names that the formatter should skip.

This is useful for generated files or files that you intentionally keep in a different format. Files produced by pre-build are already skipped automatically, so formatter.ignore is mainly for additional files you want to exclude.

options(
  formatter: {
    ignore: [ "generated.mbt", "snapshot.mbt" ],
  },
)
{
  "formatter": {
    "ignore": ["generated.mbt", "snapshot.mbt"]
  }
}

is-main#

The is-main field is used to specify whether a package needs to be linked into an executable file.

The output of the linking process depends on the backend. When this field is set to true:

  • For the Wasm and wasm-gc backends, a standalone WebAssembly module will be generated.

  • For the js backend, a standalone JavaScript file will be generated.

options(
  "is-main": true,
)
{
  "is-main": true
}

Importing dependencies#

Import#

The import field is used to specify other packages that a package depends on.

For example, the following imports pkgA and pkgC, aliasing pkgC to c. User can write @c to access definitions from pkgC.

import {
  "moonbit-community/language/packages/pkgA",
  "moonbit-community/language/packages/pkgC" @c,
  "moonbitlang/core/builtin",
}
{
    "import": [
        "moonbit-community/language/packages/pkgA",
        {
            "path": "moonbit-community/language/packages/pkgC",
            "alias": "c"
        },
        "moonbitlang/core/builtin"
    ]
}

Most core packages are not special here: if you use @json, @test, or other ordinary core aliases, add the corresponding moonbitlang/core/... package to import to avoid core_package_not_imported warnings.

prelude is the exception. It is available by default, so the names it exposes do not need an explicit package import.

Test import#

The test import is used to specify other packages that the black-box test package of this package depends on, with the same format as import.

import {
  "path/to/package1",
  "path/to/package2" @pkg2,
} for "test"
{
  "test-import": {
    "path/to/package1",
    {
      "path": "path/to/package2",
      "alias": "pkg2"
    }
  }
}

The test-import-all field is used to specify whether all public definitions from the package being tested should be imported (true) by default.

White-box test import#

The white-box test import is used to specify other packages that the white-box test package of this package depends on, with the same format as import.

import {
  "path/to/package1",
  "path/to/package2" @pkg2,
} for "wbtest"
{
  "wbtest-import": {
    "path/to/package1",
    {
      "path": "path/to/package2",
      "alias": "pkg2"
    }
  }
}

Maximum Concurrent Tests#

The max-concurrent-tests field limits how many tests from this package may run at the same time when moon test executes the package.

This is useful when tests in the same package share ports, temporary files, or other external resources that should not all run in parallel.

options(
  "max-concurrent-tests": 2,
)
{
  "max-concurrent-tests": 2
}

Conditional Compilation#

The smallest unit of conditional compilation is a file.

In a conditional compilation expression, three logical operators are supported: and, or, and not, where the or operator can be omitted.

For example, ["or", "wasm", "wasm-gc"] can be simplified to ["wasm", "wasm-gc"].

Conditions in the expression can be categorized into backends and optimization levels:

  • Backend conditions: "wasm", "wasm-gc", and "js"

  • Optimization level conditions: "debug" and "release"

Conditional expressions support nesting.

If a file is not listed in "targets", it will be compiled under all conditions by default.

Example:

options(
  targets: {
    "only_js.mbt": ["js"],
    "only_wasm.mbt": ["wasm"],
    "only_wasm_gc.mbt": ["wasm-gc"],
    "all_wasm.mbt": ["wasm", "wasm-gc"],
    "not_js.mbt": ["not", "js"],
    "only_debug.mbt": ["debug"],
    "js_and_release.mbt": ["and", ["js"], ["release"]],
    "js_only_test.mbt": ["js"],
    "js_or_wasm.mbt": ["js", "wasm"],
    "wasm_release_or_js_debug.mbt": ["or", ["and", "wasm", "release"], ["and", "js", "debug"]]
  }
)
{
  "targets": {
    "only_js.mbt": ["js"],
    "only_wasm.mbt": ["wasm"],
    "only_wasm_gc.mbt": ["wasm-gc"],
    "all_wasm.mbt": ["wasm", "wasm-gc"],
    "not_js.mbt": ["not", "js"],
    "only_debug.mbt": ["debug"],
    "js_and_release.mbt": ["and", ["js"], ["release"]],
    "js_only_test.mbt": ["js"],
    "js_or_wasm.mbt": ["js", "wasm"],
    "wasm_release_or_js_debug.mbt": ["or", ["and", "wasm", "release"], ["and", "js", "debug"]]
  }
}

Supported Targets#

The supported-targets option declares which backends a package is intended to support. It uses a target-set expression, not an array:

options(
  "supported-targets": "js",
)
{
  "supported-targets": "js"
}

Examples:

  • js for a single backend

  • +js+wasm-gc for an explicit set of backends

  • +all-js for all backends except js

Legacy array syntax is still accepted for compatibility:

{
  "supported-targets": ["js", "native"]
}

This is package metadata, not a conditional compilation rule:

  • use supported-targets to declare the package's supported backend set

  • use targets to include or exclude individual files for different backends

  • use preferred-target in moon.mod.json to choose the default backend for commands such as moon check, moon run, and moon build

When both the module and the package declare supported-targets, the effective backend set is their intersection.

Command behavior follows the selected backend:

  • moon check, moon build, moon test, and moon bench keep only packages that support the selected backend

  • moon run requires the selected package to support the selected backend

  • moon info skips unsupported selected packages with a warning

  • moon bundle skips package targets that do not support the selected backend

After root selection, Moon also checks reachable required dependencies. If a required dependency does not support the selected backend, the command fails with a normal user-facing error.

Notes:

  • omitting supported-targets means all backends are supported

  • --target all expands to wasm, wasm-gc, js, and native, but not llvm

  • llvm is still a valid supported-targets value

  • legacy array syntax is deprecated, but still accepted for compatibility

A common setup is:

  • mark a native-only package with "supported-targets": "native"

  • set "preferred-target": "native" in moon.mod.json

  • use targets only when some files inside the package differ by backend

Native Stub Files#

The native-stub field lists C stub source files that should be compiled with this package for native builds.

This is commonly used together with extern "C" declarations in the FFI documentation, where the stub file provides wrapper functions or adapter code that is easier to write in C than directly in MoonBit.

Paths are relative to the package directory.

options(
  "native-stub": [ "stub.c", "helpers.c" ],
)
{
  "native-stub": ["stub.c", "helpers.c"]
}

Rule and dev_build#

rule declares a reusable command, and dev_build applies a rule to concrete input and output files. These pre-build steps run before development commands such as moon check, moon build, and moon test.

This mechanism is intended for package authors during package development. When the package is used as a dependency by downstream users, these pre-build steps are not triggered for security reasons, so dependencies do not execute arbitrary commands during builds. Commit the generated output files to the repository so downstream users can build against them directly.

rule(name: "...", command: "...") declares a reusable command template. The name field identifies the rule, and command is a shell command string. The command can refer to $input and $output, which are supplied by the dev_build entry that uses the rule. A package can declare multiple rule entries.

dev_build(rule: "...", input: "...", output: "...") declares a pre-build step. It selects a rule and supplies the input and output paths used when expanding that rule's command. A package can declare multiple dev_build entries.

Rules can be declared either as package-level rules in the same moon.pkg or as module-level rules in moon.mod. A package-level rule is visible only to dev_build entries in that same moon.pkg; a module-level rule is visible to dev_build entries in every package in the module. When resolving a rule name, moon first looks for a package-level rule in the same moon.pkg, then for a module-level rule in moon.mod.

rule(name: "copy", command: "cat $input > $output")
dev_build(rule: "copy", input: "a.txt", output: "a.mbt")

The rule and dev_build entries are not supported in moon.pkg.json. Use the deprecated pre-build configuration instead:

{
  "pre-build": [
    {
      "input": "a.txt",
      "output": "a.mbt",
      "command": "cat $input > $output"
    }
  ]
}

In this example, running a development command such as moon check copies the contents of a.txt to a.mbt before the package is checked.

Warnings List#

Used to disable warnings, enable warnings, or treat a warning as a fatal error. The warning list is a string composed of multiple warning name, each prefixed with a sign:

  • - to disable the warning

  • + to enable the warning

  • @ to treat the enabled warning as a fatal error

For example, in the following configuration, -unused_value disables the unused functions and variables warning.

warnings = "-unused_value"
{
  "warn-list": "-unused_value"
}

If multiple warnings need to be disabled, they can be directly connected and combined.

warnings = "-unused_value-unreachable_code"
{
  "warn-list": "-unused_value-unreachable_code"
}

If it is necessary to activate certain warnings that were originally prohibited, use the plus sign.

warnings = "+unused_optional_argument"
{
  "warn-list": "+unused_optional_argument"
}

To treat a warning as a fatal error, use the @.

warnings = "@deprecated"
{
  "warn-list": "@deprecated"
}

You can also use warning numbers in warn-list. In the output below, mnemonic is the symbolic warning name used in warning lists, while id is the numeric form of the same warning.

The current list from moonc check -warn-help is:

Available warnings:
mnemonic                   description                                                     id state
unused_value               Unused variable or function.                                     1 warn
unused_value               Unused variable.                                                 2 warn
unused_type_declaration    Unused type declaration.                                         3 warn
missing_priv               Unused abstract type.                                            4 warn
unused_type_variable       Unused type variable.                                            5 warn
unused_constructor         Unused constructor.                                              6 warn
unused_field               Unused field or constructor argument.                            7 warn
redundant_modifier         Redundant modifier.                                              8 warn
struct_never_constructed   Struct never constructed.                                        9 warn
unused_pattern             Unused pattern.                                                 10 warn
partial_match              Partial pattern matching.                                       11 error
unreachable_code           Unreachable code.                                               12 warn
unresolved_type_variable   Unresolved type variable.                                       13 warn
alert or alert_<category>  All alerts or alerts with specific category.                    14 warn
unused_mut                 Unused mutability.                                              15 error
parser_inconsistency       Parser inconsistency check.                                     16 warn
ambiguous_loop_argument    Ambiguous usage of loop argument.                               17 warn
useless_loop               Useless loop expression.                                        18 warn
deprecated                 Deprecated API usage.                                           20 warn
missing_pattern_arguments  Some arguments of constructor are omitted in pattern.           21 warn
ambiguous_block            Ambiguous block.                                                22 warn
unused_try                 Useless try expression.                                         23 warn
unused_error_type          Useless error type.                                             24 warn
test_unqualified_package   Using implicitly imported API in test.                          25 off
unused_catch_all           Useless catch all.                                              26 warn
deprecated_syntax          Deprecated syntax.                                              27 warn
todo                       Todo                                                            28 warn
unused_package             Unused package.                                                 29 warn
missing_package_alias      Empty package alias.                                            30 warn
unused_optional_argument   Optional argument never supplied.                               31 off
unused_default_value       Default value of optional argument never used.                  32 off
text_segment_excceed       Text segment exceed the line or column limits.                  33 warn
implicit_use_builtin       Implicit use of definitions from `moonbitlang/core/builtin`.    34 warn
reserved_keyword           Reserved keyword.                                               35 warn
loop_label_shadowing       Loop label shadows another label.                               36 warn
unused_loop_label          Unused loop label.                                              37 warn
missing_invariant          For-loop is missing an invariant.                               38 off
missing_reasoning          For-loop is missing a proof_reasoning.                          39 off
multiline_string_escape    Deprecated escape sequence in multiline string.                 40 error
missing_rest_mark          Missing `..` in map pattern.                                    41 warn
invalid_attribute          Invalid attribute.                                              42 warn
unused_attribute           Unused attribute.                                               43 warn
invalid_inline_wasm        Invalid inline-wasm.                                            44 error
unused_rest_mark           Useless `..` in pattern                                         46 warn
invalid_mbti               Invalid mbti file                                               47 warn
missing_definition         Unused pub definition because it does not exist in mbti file.   49 warn
method_shadowing           Local method shadows upstream method                            50 warn
ambiguous_precedence       Ambiguous operator precedence                                   51 warn
unused_loop_variable       Loop variable not updated in loop                               52 warn
unused_trait_bound         Unused trait bound                                              53 warn
ambiguous_range_direction  Ambiguous looping direction for range e1..=e2                   54 off
unannotated_ffi            Unannotated FFI param type                                      55 error
missing_pattern_field      Missing field in struct pattern                                 56 warn
missing_pattern_payload    Constructor pattern expect payload                              57 warn
unaligned_byte_access      Unaligned byte access in bits pattern                           59 warn
unused_struct_update       Unused struct update                                            60 warn
duplicate_test             Duplicate test name                                             61 warn
invalid_cascade            Calling method with non-unit return type via `..`               62 warn
syntax_lint                Syntax lint warning                                             63 warn
unannotated_toplevel_array Unannotated toplevel array                                      64 warn
prefer_readonly_array      Suggest ReadOnlyArray for read-only array literal               65 off
prefer_fixed_array         Suggest FixedArray for mutated array literal                    66 off
unused_async               Useless `async` annotation                                      67 warn
declaration_unimplemented  Declaration is unimplemented                                    68 warn
declaration_implemented    Declaration is already implemented                              69 off
deprecated_for_in_method   using `iterator()` method for `for .. in` loop.                 70 off
core_package_not_imported  Packages in `moonbitlang/core` need to be explicitly imported.  71 warn
unqualified_local_using    unqualified local using                                         72 off
unnecessary_annotation     unnecessary type annotation                                     73 off
missing_doc                Missing documentation for public definition                     74 off
A                          all warnings
state: warn = enabled, error = promoted to error, off = disabled
note: default alert exceptions: alert_unsafe=off

Note

Use moonc check -warn-help to see the list of preset compiler warnings.

Alert Warning#

Alerts are special warnings that indicate the usage of API marked with #internal attribute.

All alerts has a category associated with it, which is customized by the author of the API. You can enable or disable specific alert categories using the alert_<category> warning name, or use alert to control all alert warnings at once.

For example, in the following configuration, all warnings for alerts are treated as fatal errors, except for the unsafe category, which is disabled.

warnings = "@alert-alert_unsafe" 
{
  "warn-list": "@alert-alert_unsafe" 
}

Virtual Package#

A virtual package serves as an interface of a package that can be replaced by actual implementations.

Declarations#

The virtual field is used to declare the current package as a virtual package.

For example, the following declares a virtual package with default implementation:

options(
  virtual: {
    "has-default": true,
  },
)
{
  "virtual": {
    "has-default": true
  }
}

Implementations#

The implement field is used to declare the virtual package to be implemented by the current package.

For example, the following implements a virtual package:

options(
  implement: "moonbitlang/core/abort",
)
{
  "implement": "moonbitlang/core/abort"
}

Overriding implementations#

The overrides field is used to provide the implementations that fulfills an imported virtual package.

For example, the following overrides the default implementation of the builtin abort package with another package:

options(
  overrides: [ "moonbitlang/dummy_abort/abort_show_msg" ],
)
{
  "overrides": ["moonbitlang/dummy_abort/abort_show_msg"]
}