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.