Writing your first extension

1. Workspace Setup

In your workspace root, create or update MODULE.bazel to depend on extensions_sdk:

module(
    name = "my_extensions",
    version = "1.0.0",
)

bazel_dep(name = "extensions_sdk", version = "1.0.0")

2. Generating Boilerplate

The easiest way to get started is by using the built-in scaffolding tool. This generates both the Bazel BUILD file and the starter code for your extension:

{{#tabs }} {{#tab name="Rust" }}

bazel run @extensions_sdk//tools:scaffold -- --lang rust --name my_rust_ext

{{#endtab }} {{#tab name="Golang" }}

bazel run @extensions_sdk//tools:scaffold -- --lang go --name my_go_ext

{{#endtab }} {{#tab name="Python" }}

bazel run @extensions_sdk//tools:scaffold -- --lang python --name my_py_ext

{{#endtab }} {{#tab name="Javascript" }}

bazel run @extensions_sdk//tools:scaffold -- --lang js --name my_js_ext

{{#endtab }} {{#endtabs }}


3. Extension Rules & Implementation

{{#tabs }} {{#tab name="Rust" }}

Generated BUILD Rule

The scaffolding tool generates a BUILD file using rust_extension:

load("@extensions_sdk//:defs.bzl", "rust_extension")

package(default_visibility = ["//visibility:public"])

rust_extension(
    name = "rust_sample",
    srcs = ["src/lib.rs"],
    package_name = "moosync.sample.rust",
    display_name = "Sample Extension (Rust)",
    version = "0.0.1",
)

Implementation

Extensions are represented by the Extension trait. You need to register your extension through the init function:

#![allow(unused)]
fn main() {
struct SampleExtension;

impl Extension for SampleExtension {}
impl Accounts for SampleExtension {}
impl DatabaseEvents for SampleExtension {}
impl PlayerEvents for SampleExtension {}
impl PreferenceEvents for SampleExtension {}
impl ContextMenu for SampleExtension {}

#[unsafe(no_mangle)]
pub extern "C" fn init() {
    info!("Initializing SampleExtension");
    register_extension(Box::new(SampleExtension)).unwrap();
    info!("Initialized SampleExtension");
}
}

Build

bazel build //my_rust_ext:my_rust_ext

{{#endtab }} {{#tab name="Golang" }}

Generated BUILD Rule

The scaffolding tool generates a BUILD file using go_extension:

load("@extensions_sdk//:defs.bzl", "go_extension")

package(default_visibility = ["//visibility:public"])

go_extension(
    name = "go_sample",
    srcs = ["main.go"],
    package_name = "moosync.sample.go",
    display_name = "Sample Extension (Go)",
    version = "0.0.1",
)

Implementation

Extensions are represented by embedding api.DefaultExtension. You need to register your extension through the entry function:

type SampleExtension struct {
	api.DefaultExtension
}

//go:wasmexport entry
func entry() {
	extension := &SampleExtension{}
	api.RegisterExtension(extension)
}

func main() {}

Build

bazel build //my_go_ext:my_go_ext

{{#endtab }} {{#tab name="Python" }}

Generated BUILD Rule

The scaffolding tool generates a BUILD file using py_extension:

load("@extensions_sdk//:defs.bzl", "py_extension")

package(default_visibility = ["//visibility:public"])

py_extension(
    name = "py_sample",
    srcs = ["main.py"],
    main = "main.py",
    package_name = "moosync.sample.python",
    display_name = "Sample Extension (Python)",
    version = "0.0.1",
)

Implementation

All extensions must start in a module called main. Extensions are represented by the Extension class. You need to register your extension through the entry function:

class SampleExtension(Extension):
    def __init__(self):
        super().__init__()

def entry():
    print("Initializing SampleExtension")
    register_extension(SampleExtension())

Build

bazel build //my_py_ext:my_py_ext

{{#endtab }} {{#tab name="Javascript" }}

Generated BUILD Rule

The scaffolding tool generates a BUILD file using js_extension:

load("@extensions_sdk//:defs.bzl", "js_extension")

package(default_visibility = ["//visibility:public"])

js_extension(
    name = "js_sample",
    srcs = ["src/index.ts"],
    package_name = "moosync.sample.js",
    display_name = "Sample Extension (JavaScript)",
    version = "0.0.1",
)

Implementation

You need to re-export all methods provided by wasm-extension-js package. The entrypoint of your extension is a function called entry:

export function entry(): number {
  const api = getApi();

  console.log("Initialized sample extension");
  return 0;
}

Build

bazel build //my_js_ext:my_js_ext

{{#endtab }} {{#endtabs }}