Skip to main content

User Macros

User macros are custom Flamework-style macros that can be defined by user code. They allow you to tap into metadata about type parameters and the call site.

Defining a user macro

You define a macro simply by using the @metadata macro jsdoc and using one of Flamework's utility types as a parameter. You can also pass down generics from other user macros into this macro by passing a Modding.Generic/Caller with the necessary metadata.

This can be defined on methods, classes (for new A()) and is also supported everywhere that the @metadata tag is, e.g in lifecycle events.

/** @metadata macro */
function macro<T>(abc?: Modding.Generic<T, "id">, xyz?: Modding.Caller<"line" | "char">) {
assert(abc && xyz);

print(abc.id, `${xyz.line}:${xyz.char}`);
}

macro<MyInterface>();

Nesting user macros

If you have a user macro that you'd like to call from within another user macro, you can pass down the Modding.Generic/Caller objects as long as they have the necessary metadata.

/** @metadata macro */
function baseMacro<T>(abc?: Modding.Generic<T, "id">) {}

/** @metadata macro */
function newMacro<T>(param: string, abc?: Modding.Generic<T, "id" | "text">) {
// do something with abc.text
return baseMacro<T>(abc);
}

Advanced User Macros

Flamework supports a more advanced form of user macros allowing you to perform arbitrary conditions, mapped types and similar at compile time. This is achieved using the Modding.Many user macro API.

I've provided a list of syntax that you can use, but it is not exhaustive and Flamework can generate most types without any issues.

Exposing your macro metadata

In libraries, it is recommended that you specify macro metadata in an interface and expose it publicly. This allows users to easily nest your macro inside of their own. This can be an interface or a type alias, but remember to only use Modding.Many under the actual macro.

export interface DoSomethingMacro<T> {}

declare function doSomething<T>(metadata?: Modding.Many<DoSomethingMacro<T>>);

Objects / Tuples

You can provide an object or a tuple to Modding.Many and Flamework will generate an identical value when the function is called, for example:

declare function macro<T>(metadata?: Modding.Many<{ a: Modding.Generic<T, "id">, b: Modding.Caller<"uuid"> }>);
declare function macro<T>(metadata?: Modding.Many<[Modding.Generic<T, "id">, Modding.Caller<"uuid">]>);

Arrays

Besides tuples, Flamework also supports generating arrays using de-unification. This means that you are able to turn a union (e.g keyof T) into an array of all constituents.

declare function macro<T>(keysOfT?: Modding.Many<(keyof T)[]>);

Mapped types

You are able to use mapped types to generate derivatives of an object which can be used to fetch additional information about members of a type.

declare function macro<T>(guardsForEachMember?: Modding.Many<{ [k in keyof T]: Modding.Generic<T[k], "guard"> }>);

Literals / Conditionals

You can use conditional types to simulate if statements at compile-time. Flamework supports generating most literal values such as numbers, strings, booleans and undefined.

declare function macro<T>(isString?: Modding.Many<T extends string ? true : false>);

Utility types

Flamework provides some additional utility types besides Modding.Generic and Modding.Caller which you can find here.

Modding.Hash

Modding.Hash allows you to generate a UUID based off a string (and an optional context.)

Modding.Obfuscate

This behaves identically to Modding.Hash except it is only enabled when Flamework obfuscation is enabled.

Modding.TupleLabels

This retrieves the labels of a tuple and can be used in conjunction with Parameters<T> to retrieve parameter names from a function type.

Generic Metadata

You can access generic metadata by using Modding.Generic<T, M>. T does not have to be a type parameter and could contain any type, e.g keyof T or { [k in keyof T]: string }.

id

The ID of the T.

guard

An automatically generated type guard for T. This function is also typed as t.check<T> meaning you can use it without casting.

text

The text equivalent of T.

Callsite Metadata

You can access callsite metadata by using Modding.Caller<M>. Metadata about the source text ignores leading and trailing trivia.

line

The line number that this call is on, starting at 1.

char

The character that this call is on, starting at 1.

width

The length of the expression's text.

text

The text of the expression.

uuid

A randomly generated universally unique identifier. This can be used to identify a specific callsite.