mbts

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

moon add mizchi/mbts@0.0.3
Download zip
Author
Version
0.0.3
License
MIT
Last updated
6 months ago
Downloads
20

Dependencies

README

#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)