Sign in

    mbts

    well-typed mbt(i) to d.ts converter and utils

    Download zip
    Author
    Version
    0.0.3
    License
    MIT
    Last updated
    8 months ago
    Downloads
    33

    Dependencies

    #mbts

    Bidirectional type definition converter between MoonBit and TypeScript.

    deno install -Afg @mizchi/mbts --name mbts # TODO: npm

    #Features

    • Create new MoonBit projects with JS interop setup
    • Full build pipeline (check, build, link, dts)
    • .mbti → .d.ts generation
    • .d.ts → .mbt / .mbti generation
    • Auto-update moon.pkg.json exports

    #Installation

    # As a CLI tool pnpm add mbts # Or use directly npx mbts --help

    #Quick Start

    # Create a new project mbts new myapp --user myname cd myapp moon update # Build and generate TypeScript definitions mbts build . # Use from JavaScript node -e "const m = require('./target/js/release/build/myapp/myapp.js'); console.log(m.hello())"

    #CLI Commands

    #mbts new - Create a new project

    mbts new <pkgname> --user <username>

    Creates a new MoonBit project with:
    • moon.mod.json - Module config with mizchi/js dependency and preferred-target: js
    • moon.pkg.json - Package config with JS link settings
    • lib.mbt - Sample source file

    # Create project mbts new myapp --user myname # Without --user (uses "username" as default with warning) mbts new myapp

    #mbts build - Full build pipeline

    mbts build <path>

    Runs the complete build pipeline:
    1. moon check - Type checking
    2. moon build --target js - Build for JS target
    3. moon info - Generate .mbti
    4. mbts link - Update exports in moon.pkg.json
    5. mbts dts - Generate .d.ts

    # Build current directory mbts build . # Build specific directory mbts build examples # With options mbts build . --out js --naming camelCase

    mbts link <path>

    Updates moon.pkg.json with exports from .mbti file. Also generates __jsglue.mbt for generic function wrappers.

    # Update exports after running 'moon info' moon info mbts link . # Multiple targets mbts link . --targets js,wasm-gc # Exclude methods mbts link . --no-methods # Dry run mbts link . --dry-run

    #mbts dts - Generate TypeScript definitions

    mbts dts <src> [--out <dir>]

    Generates .d.ts from .mbti file.

    # Generate .d.ts in same directory mbts dts . # Output to different directory mbts dts src --out js # Wrap in namespace mbts dts src --namespace # Include runtime type preamble mbts dts src --preamble # Convert function names to camelCase mbts dts src --naming camelCase

    #mbts mbt - Generate MoonBit from TypeScript

    mbts mbt <file.d.ts> [options]

    Generates MoonBit FFI bindings from TypeScript definitions.

    # Basic conversion mbts mbt lib.d.ts # Specify output directory and package name mbts mbt lib.d.ts --out src --package myapp # Also generate .mbti interface file mbts mbt lib.d.ts --mbti

    #Type Mapping

    #MoonBit → TypeScript

    MoonBitTypeScript
    Stringstring
    Int, UInt, Float, Doublenumber
    Boolboolean
    Unitvoid
    BytesUint8Array
    BigIntbigint
    Array[T]Array<T>
    Map[K, V]Map<K, V>
    Jsonany
    T? / Option[T]T \| undefined
    (A, B, C)[A, B, C]

    #TypeScript → MoonBit

    TypeScriptMoonBit
    stringString
    numberInt
    booleanBool
    voidUnit
    Uint8ArrayBytes
    bigintBigInt
    T[] / Array<T>Array[T]
    Map<K, V>@collection.JsMap[K, V]
    Set<T>@collection.JsSet[T]
    Promise<T>@js.Promise[T]
    T \| undefinedT?
    any / unknownJson

    #Structs

    // MoonBit
    pub struct User {
    name : String
    age : Int
    mut email : String
    }

    // TypeScript export interface User { readonly name: string; readonly age: number; email: string; }

    #Enums

    // MoonBit
    pub enum Status {
    Pending
    Active
    Done
    }

    // TypeScript (Discriminated Union) export interface Status_Pending { readonly $tag: "Pending"; } export interface Status_Active { readonly $tag: "Active"; } export interface Status_Done { readonly $tag: "Done"; } export type Status = Status_Pending | Status_Active | Status_Done;

    #Generic Types

    // MoonBit - Generic function (needs wrapper for JS export)
    pub fn[T] identity(value : T) -> T {
    value
    }

    // FFI functions must use @js.Any (no type parameters allowed)
    extern "js" fn Box::new(value : @js.Any) -> Box[@js.Any] =
    #| (v) => ({ value: v })

    When you run mbts link, it automatically generates wrappers in __jsglue.mbt:

    // Auto-generated
    pub fn __jsglue_identity(arg0 : @js.Any) -> @js.Any {
    identity(arg0)
    }

    #Conversion Rules

    #Function Names

    MoonBitTypeScript (default)TypeScript (--naming camelCase)
    get_user_nameget_user_namegetUserName
    Type::methodtype$methodtype$method

    #Structs → Interfaces

    pub struct User {
    id : Int // immutable
    mut name : String // mutable
    }

    export interface User { readonly id: number; // readonly (immutable) name: string; // writable (mut) }

    #Enums → Discriminated Unions

    pub enum Result[T, E] {
    Ok(T)
    Err(E)
    }

    export interface Result_Ok<T> { readonly $tag: "Ok"; readonly $0: T; } export interface Result_Err<E> { readonly $tag: "Err"; readonly $0: E; } export type Result<T, E> = Result_Ok<T> | Result_Err<E>;

    #Option Types

    pub fn find(id : Int) -> User?

    export function find(id: number): User | undefined;

    #Tuples

    pub fn get_pair() -> (Int, String)

    export function get_pair(): [number, string];

    #Methods

    Methods are exported with $ separator:

    pub fn User::new(name : String) -> User
    pub fn User::greet(self : User) -> String

    export function user$new(name: string): User; export function user$greet(self: User): string;

    #Programmatic API

    #.mbti → .d.ts

    import { generateDts } from "mbts"; const mbtiContent = ` package "myapp" pub struct User { name : String age : Int } pub fn get_user(id : Int) -> User `; const dts = generateDts(mbtiContent, "myapp.mbti");

    #.d.ts → .mbt / .mbti

    import { parseDts, generateMbt, generateMbti } from "mbts"; const dtsContent = ` export interface User { name: string; age: number; } export function createUser(name: string): User; `; const binding = parseDts(dtsContent, "lib.d.ts", { packageName: "myapp" }); const mbt = generateMbt(binding); const mbti = generateMbti(binding);

    #Important Notes

    1. extern "js" fn cannot have type parameters - Use @js.Any instead
    2. Generic functions need wrappers for JS export - mbts link generates these automatically
    3. Always run moon info before mbts link/dts - The .mbti file must be up to date
    4. Set preferred-target: js in moon.mod.json for JS-only projects

    #Development

    # Install dependencies moon update pnpm install # Build CLI pnpm build:cli # Run tests moon test pnpm test

    #Internals

    mbts uses an internal .mbti parser to analyze MoonBit's interface files. The parser extracts:

    • Function signatures with type parameters
    • Struct and enum definitions
    • Method declarations

    For generic functions that cannot be directly exported to JavaScript (due to MoonBit's FFI limitation), mbts automatically generates wrapper functions in __jsglue.mbt. These wrappers replace type parameters with @js.Any, enabling JavaScript interop while preserving the original generic implementation.

    .mbti (parsed) → AST → analyze generics → generate __jsglue.mbt

    #License

    MIT

    MbtBinding

    pub struct MbtBinding {
    package_name : String
    types : Array[MbtType]
    functions : Array[MbtFunction]
    extern_types : Array[String]
    classes : Array[MbtClass]
    }

    MbtBinding::from_json

    fn MbtBinding::from_json(json : Json) -> MbtBinding?

    Parse MbtBinding from JSON JSON structure mirrors dts-to-mbt.ts MbtBinding interface

    MbtBinding::new

    fn MbtBinding::new() -> MbtBinding

    MbtClass

    pub struct MbtClass {
    name : String
    type_params : Array[String]
    methods : Array[MbtMethod]
    has_constructor : Bool
    }

    MbtClass::from_json

    fn MbtClass::from_json(json : Json) -> MbtClass?

    MbtField

    pub struct MbtField {
    name : String
    type_ : String
    mutable : Bool
    }

    MbtField::from_json

    fn MbtField::from_json(json : Json) -> MbtField?

    MbtField::new

    fn MbtField::new(name : String, type_ : String) -> MbtField

    MbtFunction

    pub struct MbtFunction {
    name : String
    params : Array[MbtParam]
    return_type : String
    is_async : Bool
    js_name : String
    type_params : Array[String]
    is_method : Bool
    class_name : String
    }

    MbtFunction::from_json

    fn MbtFunction::from_json(json : Json) -> MbtFunction?

    MbtFunction::new

    fn MbtFunction::new(name : String, js_name : String) -> MbtFunction

    MbtMethod

    pub struct MbtMethod {
    name : String
    js_name : String
    }

    MbtMethod::from_json

    fn MbtMethod::from_json(json : Json) -> MbtMethod?

    MbtParam

    pub struct MbtParam {
    name : String
    type_ : String
    optional : Bool
    }

    MbtParam::from_json

    fn MbtParam::from_json(json : Json) -> MbtParam?

    MbtParam::new

    fn MbtParam::new(name : String, type_ : String) -> MbtParam

    MbtType

    pub struct MbtType {
    name : String
    kind : MbtTypeKind
    fields : Array[MbtField]
    variants : Array[MbtVariant]
    type_params : Array[String]
    js_name : String
    }

    MbtType::from_json

    fn MbtType::from_json(json : Json) -> MbtType?

    MbtType::new

    fn MbtType::new(name : String, kind : MbtTypeKind) -> MbtType

    MbtTypeKind

    pub enum MbtTypeKind {
    Struct
    Type
    Enum
    }

    MbtVariant

    pub struct MbtVariant {
    name : String
    payload : Array[String]
    }

    MbtVariant::from_json

    fn MbtVariant::from_json(json : Json) -> MbtVariant?

    MbtVariant::new

    fn MbtVariant::new(name : String) -> MbtVariant

    format_fields

    fn format_fields(fields : Array[MbtField]) -> String

    Format struct fields

    format_fn_params

    fn format_fn_params(params : Array[MbtParam]) -> String

    Format function parameters as "name : Type, name2 : Type2"

    format_type_params

    fn format_type_params(params : Array[String]) -> String

    Format type parameters as "[T, U, V]" or "" if empty

    format_variants

    fn format_variants(variants : Array[MbtVariant]) -> String

    Format enum variants

    generate_combined_dts

    fn generate_combined_dts(packages : Array[
    Mbti
    ]) -> String

    Generate combined TypeScript .d.ts from multiple MBTI packages

    generate_dts

    fn generate_dts(mbti :
    Mbti
    ) -> String

    Generate TypeScript .d.ts content from MBTI AST

    generate_dts_from_mbt

    fn generate_dts_from_mbt(content : String, filename : String) -> String

    Generate TypeScript .d.ts from .mbt source code

    generate_dts_namespace

    fn generate_dts_namespace(mbti :
    Mbti
    ) -> String

    Generate TypeScript .d.ts content wrapped in a namespace

    generate_dts_with_preamble

    fn generate_dts_with_preamble(mbti :
    Mbti
    , include_preamble~ : Bool) -> String

    Generate TypeScript .d.ts with optional preamble

    generate_mbt

    fn generate_mbt(binding : MbtBinding) -> String

    package_to_namespace

    fn package_to_namespace(package_name : String) -> String

    Extract namespace name from package name e.g., "moonbitlang/parser/basic" -> "basic"

    parse_mbt

    Parse .mbt content and return the AST

    parse_mbti

    fn parse_mbti(content : String, filename : String) ->
    Mbti
    raise

    Parse MBTI content and return the AST

    preprocess_mbti

    fn preprocess_mbti(content : String) -> String

    Preprocess MBTI content to remove unsupported syntax (pub impl and pub using are not yet supported by mbti_parser)