---
title: Extensions
tags: [extension, wasm, plugin]
toc_max_heading_level: 4
---

import VersionLabel from '@site/src/components/Docs/VersionLabel';

<VersionLabel version="1.20.0" header />

An extension is a WASM plugin that allows you to extend moon with additional functionality, have
whitelisted access to the file system, and receive partial information about the current workspace.
Extensions are extremely useful in offering new and unique functionality that doesn't need to be
built into moon's core. It also enables the community to build and share their own extensions!

## Using extensions

Before an extension can be executed with the [`moon ext`](../commands/ext) command, it must be
configured with [`.moon/extensions.*`](../config/extensions) (excluding
[built-in's](#built-in-extensions)).

```yaml title=".moon/extensions.yml"
example:
  plugin: 'https://example.com/path/to/example.wasm'
```

Once configured, it can be executed with [`moon ext`](../commands/ext) by name. Arguments unique to
the extension _must_ be passed after a `--` separator.

```shell
$ moon ext example -- --arg1 --arg2
```

## Built-in extensions

moon is shipped with a few built-in extensions that are configured and enabled by default. Official
moon extensions are built and published in our [moonrepo/moon-extensions][repo] repository.

### `download`

The `download` extension can be used to download a file from a URL into the current workspace, as
defined by the `--url` argument. For example, say we want to download the latest [proto](/proto)
binary:

```shell
$ moon ext download --\
  --url https://github.com/moonrepo/proto/releases/latest/download/proto_cli-aarch64-apple-darwin.tar.xz
```

By default this will download `proto_cli-aarch64-apple-darwin.tar.xz` into the current working
directory. To customize the location, use the `--dest` argument. However, do note that the
destination _must be_ within the current moon workspace, as only certain directories are whitelisted
for WASM.

```shell
$ moon ext download --\
  --url https://github.com/moonrepo/proto/releases/latest/download/proto_cli-aarch64-apple-darwin.tar.xz\
  --dest ./temp
```

#### Arguments

- `--url` (required) - URL of a file to download.
- `--dest` - Destination folder to save the file. Defaults to the current working directory.
- `--name` - Override the file name. Defaults to the file name in the URL.

### `migrate-nx`<VersionLabel version="1.22.0" />

> This extension is currently _experimental_ and will be improved over time.

The `migrate-nx` extension can be used to migrate an Nx powered repository to moon. This process
will convert the root `nx.json` and `workspace.json` files, and any `project.json` and
`package.json` files found within the repository. The following changes are made:

- Migrates `targetDefaults` as global tasks to [`.moon/tasks/node.yml`](../config/tasks#tasks) (or
  `bun.yml`), `namedInputs` as file groups, `workspaceLayout` as projects, and more.
- Migrates all `project.json` settings to [`moon.yml`](../config/project#tasks) equivalent settings.
  Target to task conversion assumes the following:
  - Target `executor` will be removed, and we'll attempt to extract the appropriate npm package
    command. For example, `@nx/webpack:build` -> `webpack build`.
  - Target `options` will be converted to task `args`.
  - The `{projectRoot}` and `{workspaceRoot}` interpolations will be replaced with moon tokens.

```shell
$ moon ext migrate-nx
```

:::caution

Nx and moon are quite different, so many settings are either ignored when converting, or are not a
1:1 conversion. We do our best to convert as much as possible, but some manual patching will most
likely be required! We suggest testing each converted task 1-by-1 to ensure it works as expected.

:::

#### Arguments

- `--bun` - Migrate to Bun based commands instead of Node.js.
- `--cleanup` - Remove Nx configs/files after migrating.

#### Unsupported

The following features are not supported in moon, and are ignored when converting.

- Most settings in `nx.json`.
- Named input variants: external dependencies, dependent task output files, dependent project
  inputs, or runtime commands.
- Target `configurations` and `defaultConfiguration`. Another task will be created instead that uses
  `extends`.
- Project `root` and `sourceRoot`.

### `migrate-turborepo`<VersionLabel version="1.21.0" />

The `migrate-turborepo` extension can be used to migrate a Turborepo powered repository to moon.
This process will convert the root `turbo.json` file, and any `turbo.json` files found within the
repository. The following changes are made:

- Migrates `pipeline` (v1) and `tasks` (v2) global tasks to
  [`.moon/tasks/node.yml`](../config/tasks#tasks) (or `bun.yml`) and project scoped tasks to
  [`moon.*`](../config/project#tasks). Task commands will execute `package.json` scripts through a
  package manager.
- Migrates root `global*` settings to [`.moon/tasks/node.yml`](../config/tasks#implicitinputs) (or
  `bun.yml`) as `implicitInputs`.

```shell
$ moon ext migrate-turborepo
```

#### Arguments

- `--bun` - Migrate to Bun based commands instead of Node.js.
- `--cleanup` - Remove Turborepo configs/files after migrating.

### `unpack`<VersionLabel version="2.0.0" />

The `unpack` extension can be used to unpack an archive (zip/tar) from a file path or URL into a
destination folder.

```shell
$ moon ext unpack -- --src ./path/to/archive.zip --dest ./output --prefix path/to/strip
```

#### Arguments

- `--src` (required) - Path or URL of a file to unpack.
- `--dest` - Destination folder to unpack into. Defaults to the current working directory.
- `--prefix` - A prefix path to strip from unpacked files.

## Creating an extension

Refer to our [official WASM guide](./wasm-plugins) for more information on how our WASM plugins
work, critical concepts to know, how to create a plugin, and more. Once you have a good
understanding, you may continue this specific guide.

:::note

Refer to our [moonrepo/plugins][repo] repository for in-depth examples.

:::

### Registering metadata

Before we begin, we must implement the `register_extension` function, which simply provides some
metadata that we can bubble up to users, or to use for deeper integrations.

```rust
use extism_pdk::*;
use moon_pdk::*;

#[plugin_fn]
pub fn register_extension(Json(input): Json<ExtensionMetadataInput>) -> FnResult<Json<ExtensionMetadataOutput>> {
   Ok(Json(ExtensionMetadataOutput {
        name: "Extension name".into(),
        description: Some("A description about what the extension does.".into()),
        plugin_version: env!("CARGO_PKG_VERSION").into(),
        ..ExtensionMetadataOutput::default()
    }))
}
```

#### Configuration schema

If you are using [configuration](#supporting-configuration), you can register the shape of the
configuration using the [`schematic`](https://crates.io/crates/schematic) crate. This shape will be
used to generate outputs such as JSON schemas, or TypeScript types.

```rust
#[plugin_fn]
pub fn define_extension_config() -> FnResult<Json<DefineExtensionConfigOutput>> {
    Ok(Json(DefineExtensionConfigOutput {
        schema: schematic::SchemaBuilder::build_root::<ExampleExtensionConfig>(),
    }))
}
```

Schematic is a heavy library, so we suggest adding the dependency like so:

```toml
[dependencies]
schematic = { version = "*", default-features = false, features = ["schema"] }
```

### Implementing execution

Extensions support a single plugin function, `execute_extension`, which is called by the
[`moon ext`](../commands/ext) command to execute the extension. This is where all your business
logic will reside.

```rust
#[host_fn]
extern "ExtismHost" {
    fn host_log(input: Json<HostLogInput>);
}

#[plugin_fn]
pub fn execute_extension(Json(input): Json<ExecuteExtensionInput>) -> FnResult<()> {
  host_log!(stdout, "Executing extension!");

  Ok(())
}
```

### Supporting arguments

Most extensions will require arguments, as it provides a mechanism for users to pass information
into the WASM runtime. To parse arguments, we provide the
[`Args`](https://docs.rs/clap/latest/clap/trait.Args.html) trait/macro from the
[clap](https://crates.io/crates/clap) crate. Refer to their
[official documentation on usage](https://docs.rs/clap/latest/clap/_derive/index.html) (we don't
support everything).

```rust
use moon_pdk::*;

#[derive(Args)]
pub struct ExampleExtensionArgs {
  // --url, -u
  #[arg(long, short = 'u', required = true)]
  pub url: String,
}
```

Once your struct has been defined, you can parse the provided input arguments using the
[`parse_args`](https://docs.rs/moon_pdk/latest/moon_pdk/args/fn.parse_args.html) function.

```rust
#[plugin_fn]
pub fn execute_extension(Json(input): Json<ExecuteExtensionInput>) -> FnResult<()> {
  let args = parse_args::<ExampleExtensionArgs>(&input.args)?;

  args.url; // --url

  Ok(())
}
```

### Supporting configuration

Users can configure [extensions](../config/workspace#extensions) with additional settings in
[`.moon/extensions.*`](../config/extensions). Do note that settings should be in camelCase for them
to be parsed correctly!

```yaml title=".moon/extensions.yml"
example:
  plugin: 'file://./path/to/example.wasm'
  someSetting: 'abc'
  anotherSetting: 123
```

In the plugin, we can map these settings (excluding `plugin`) into a struct. The `Default` trait
must be implemented to handle situations where settings were not configured, or some are missing.

```rust
config_struct!(
  #[derive(Default)]
  pub struct ExampleExtensionConfig {
    pub some_setting: String,
    pub another_setting: u32,
  }
);
```

Once your struct has been defined, you can access the configuration using the
[`get_extension_config`](https://docs.rs/moon_pdk/latest/moon_pdk/extension/fn.get_extension_config.html)
function.

```rust
#[plugin_fn]
pub fn execute_extension(Json(input): Json<ExecuteExtensionInput>) -> FnResult<()> {
  let config = get_extension_config::<ExampleExtensionConfig>()?;

  config.another_setting; // 123

  Ok(())
}
```

[repo]: https://github.com/moonrepo/plugins
