website/docs/guides/extensions.mdx
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!
Before an extension can be executed with the moon ext command, it must be
configured with .moon/extensions.* (excluding
built-in's).
example:
plugin: 'https://example.com/path/to/example.wasm'
Once configured, it can be executed with moon ext by name. Arguments unique to
the extension must be passed after a -- separator.
$ moon ext example -- --arg1 --arg2
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 repository.
downloadThe 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
binary:
$ 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.
$ moon ext download --\
--url https://github.com/moonrepo/proto/releases/latest/download/proto_cli-aarch64-apple-darwin.tar.xz\
--dest ./temp
--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:
targetDefaults as global tasks to .moon/tasks/node.yml (or
bun.yml), namedInputs as file groups, workspaceLayout as projects, and more.project.json settings to moon.yml equivalent settings.
Target to task conversion assumes the following:
executor will be removed, and we'll attempt to extract the appropriate npm package
command. For example, @nx/webpack:build -> webpack build.options will be converted to task args.{projectRoot} and {workspaceRoot} interpolations will be replaced with moon tokens.$ 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.
:::
--bun - Migrate to Bun based commands instead of Node.js.--cleanup - Remove Nx configs/files after migrating.The following features are not supported in moon, and are ignored when converting.
nx.json.configurations and defaultConfiguration. Another task will be created instead that uses
extends.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:
pipeline (v1) and tasks (v2) global tasks to
.moon/tasks/node.yml (or bun.yml) and project scoped tasks to
moon.*. Task commands will execute package.json scripts through a
package manager.global* settings to .moon/tasks/node.yml (or
bun.yml) as implicitInputs.$ moon ext migrate-turborepo
--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.
$ moon ext unpack -- --src ./path/to/archive.zip --dest ./output --prefix path/to/strip
--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.Refer to our official WASM guide 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 repository for in-depth examples.
:::
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.
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()
}))
}
If you are using configuration, you can register the shape of the
configuration using the schematic crate. This shape will be
used to generate outputs such as JSON schemas, or TypeScript types.
#[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:
[dependencies]
schematic = { version = "*", default-features = false, features = ["schema"] }
Extensions support a single plugin function, execute_extension, which is called by the
moon ext command to execute the extension. This is where all your business
logic will reside.
#[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(())
}
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 trait/macro from the
clap crate. Refer to their
official documentation on usage (we don't
support everything).
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 function.
#[plugin_fn]
pub fn execute_extension(Json(input): Json<ExecuteExtensionInput>) -> FnResult<()> {
let args = parse_args::<ExampleExtensionArgs>(&input.args)?;
args.url; // --url
Ok(())
}
Users can configure extensions with additional settings in
.moon/extensions.*. Do note that settings should be in camelCase for them
to be parsed correctly!
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.
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
function.
#[plugin_fn]
pub fn execute_extension(Json(input): Json<ExecuteExtensionInput>) -> FnResult<()> {
let config = get_extension_config::<ExampleExtensionConfig>()?;
config.another_setting; // 123
Ok(())
}