UNPKG

@macroforge/mcp-server

Version:

MCP server for Macroforge documentation and code analysis

306 lines (249 loc) • 10.9 kB
# Context and IR Every macro runs with a `MacroContextIR`: what invoked it, where, and a structured view (the IR) of the declaration it is attached to. Derive macros usually read the IR through [`DeriveInput`](../../docs/custom-macros/ts-macro-derive); this page describes the context and IR types underneath, which attribute macros use directly. ## MacroContextIR A macro gets its context from its input stream with `input.context()`, or from `DeriveInput`'s `context` field. Rust ``` pub struct MacroContextIR {     pub macro_kind: MacroKind,        // Derive, Attribute or Call     pub macro_name: String,           // e.g. "Debug"     pub module_path: String,          // the module the macro was imported from     pub decorator_span: SpanIR,       // the whole @derive(...) or @attr(...)     pub macro_name_span: Option<SpanIR>, // just the macro's name in it     pub target_span: SpanIR,          // the declaration the macro is attached to     pub file_name: String,            // the file being expanded     pub target: TargetIR,             // the declaration, as IR     pub target_source: String,        // the declaration's source text     pub import_registry: ImportRegistry, // the file's imports     pub config: Option<MacroforgeConfig>, // the project's macroforge.config     pub type_registry: TypeRegistry,  // every type in the project     pub resolved_fields: Option<HashMap<String, ResolvedTypeRef>>,     pub abi_version: u32, } ``` | Method | Returns | | ------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- | | `as_class()`, `as_interface()`, `as_enum()`, `as_type_alias()`, `as_function()` | The target as that kind of IR, or `None` | | `error_span()` | The macro's name if known, else the whole decorator: the place to point an error | | `import_specifier_for(type_name)` | The module the file should import a type from; see [Type-Aware Macros](../../docs/custom-macros/type-aware) | `type_registry`, `resolved_fields` and `config` are covered in [Type-Aware Macros](../../docs/custom-macros/type-aware), the import methods in [Output and Imports](../../docs/custom-macros/output#imports). Note A call macro (`$name(...)`) gets a minimal context: its target is `TargetIR::Other`, `target_source` is the text between the parentheses, and the file name, config and registries are empty. ## TargetIR Rust ``` pub enum TargetIR {     Class(ClassIR),     Interface(InterfaceIR),     Enum(EnumIR),     TypeAlias(TypeAliasIR),     Function(FunctionIR),     Other,            // a call macro's arguments, or anything unsupported } ``` An attribute macro on a class method receives the method as a `FunctionIR`. ## Type parameters Every generic declaration lists its type parameters as declared. `declaration()` renders one as a generic function would declare it, and `declare_all` and `apply_all` render a whole list as `<T extends Shape>` and `<T>`. Variance annotations (`in`, `out`) are not kept: TypeScript rejects them on a function. Rust ``` pub struct TypeParamIR {     pub name: String,     pub constraint: Option<String>,  // the type after \`extends\`     pub default: Option<String>,     // the type after \`=\`     pub is_const: bool, } ``` ## Classes Rust ``` pub struct ClassIR {     pub name: String,     pub span: SpanIR,     pub body_span: SpanIR,           // the braces and everything between them     pub is_abstract: bool,     pub type_params: Vec<TypeParamIR>, // e.g. T, K extends string     pub heritage: Vec<String>,       // extends and implements clauses     pub decorators: Vec<DecoratorIR>,     pub fields: Vec<FieldIR>,     pub methods: Vec<MethodSigIR>, } pub struct FieldIR {     pub name: String,     pub span: SpanIR,     pub ts_type: String,             // the annotation as written, e.g. "string[]"     pub optional: bool,              // declared with ?     pub readonly: bool,     pub visibility: Visibility,      // Public, Protected or Private     pub decorators: Vec<DecoratorIR>, } pub struct MethodSigIR {     pub name: String,     pub span: SpanIR,     pub type_params: Vec<TypeParamIR>,     pub params_src: String,          // the parameter list as written     pub return_type_src: String,     pub is_static: bool,     pub is_async: bool,     pub visibility: Visibility,     pub decorators: Vec<DecoratorIR>,     pub body_span: Option<SpanIR>,   // None for an abstract method     pub body_src: Option<String>, } ``` ## Interfaces Rust ``` pub struct InterfaceIR {     pub name: String,     pub span: SpanIR,     pub body_span: SpanIR,     pub type_params: Vec<TypeParamIR>,     pub heritage: Vec<String>,       // extends clauses     pub decorators: Vec<DecoratorIR>,     pub fields: Vec<InterfaceFieldIR>,     pub methods: Vec<InterfaceMethodIR>, } pub struct InterfaceFieldIR {     pub name: String,     pub span: SpanIR,     pub ts_type: String,     pub optional: bool,     pub readonly: bool,     pub decorators: Vec<DecoratorIR>, } pub struct InterfaceMethodIR {     pub name: String,     pub span: SpanIR,     pub type_params: Vec<TypeParamIR>,     pub params_src: String,     pub return_type_src: String,     pub optional: bool,     pub decorators: Vec<DecoratorIR>, } ``` ## Enums Rust ``` pub struct EnumIR {     pub name: String,     pub span: SpanIR,     pub body_span: SpanIR,     pub decorators: Vec<DecoratorIR>,     pub variants: Vec<EnumVariantIR>,     pub is_const: bool,              // declared const enum } pub struct EnumVariantIR {     pub name: String,     pub span: SpanIR,     pub value: EnumValue,     pub decorators: Vec<DecoratorIR>, } pub enum EnumValue {     String(String),   // Active = "active"     Number(f64),      // Low = 1     Auto,             // no initializer: one more than the previous member     Expr(String),     // Mask = A | B, kept as source text } ``` `EnumValue` has a test and an accessor for each kind: `is_string()`/`as_string()`, `is_number()`/`as_number()`, `is_expr()`/`as_expr()` and `is_auto()`. ## Type Aliases Rust ``` pub struct TypeAliasIR {     pub name: String,     pub span: SpanIR,     pub decorators: Vec<DecoratorIR>,     pub type_params: Vec<TypeParamIR>,     pub body: TypeBody, } pub enum TypeBody {     Union(Vec<TypeMember>),          // A | B | "c"     Intersection(Vec<TypeMember>),   // A & B     Object { fields: Vec<InterfaceFieldIR> }, // { a: string }     Tuple(Vec<String>),              // [string, number]     Alias(String),                   // Other<T>     Other(String),                   // anything else, as source text } pub struct TypeMember {     pub kind: TypeMemberKind,     pub decorators: Vec<DecoratorIR>, // JSDoc tags on a union member } pub enum TypeMemberKind {     Literal(String),                 // "active", 42     TypeRef(String),                 // User     Object { fields: Vec<InterfaceFieldIR> },     Intersection(Vec<TypeMember>), } ``` `TypeBody` has `is_union()`, `is_intersection()`, `is_object()`, `is_tuple()` and `is_alias()`, each with an `as_...()` accessor. `TypeMember` has `is_literal()`, `is_type_ref()`, `is_object()`, their `as_...()` accessors, `as_intersection_members()`, `type_name()` (the name of a literal or type reference) and `has_decorator(name)`. ## Functions Attribute macros on functions, and on class methods, receive a `FunctionIR`: Rust ``` pub struct FunctionIR {     pub name: String,     pub span: SpanIR,     pub body_span: SpanIR,     pub signature_span: SpanIR,      // from the start to the body's opening brace     pub is_async: bool,     pub is_generator: bool,     pub is_exported: bool,     pub is_default_export: bool,     pub type_params: Vec<TypeParamIR>,     pub params: Vec<FunctionParamIR>,     pub return_type_src: String,     pub body_src: String,     pub decorators: Vec<DecoratorIR>, } pub struct FunctionParamIR {     pub name: String,     pub span: SpanIR,     pub type_src: String,     pub default_src: Option<String>,     pub is_optional: bool,     pub is_rest: bool,     pub decorators: Vec<DecoratorIR>, } ``` ## Decorators Rust ``` pub struct DecoratorIR {     pub name: String,      // e.g. "endec"     pub args_src: String,  // the arguments as written, e.g. "skip, rename: \\"id\\""     pub span: SpanIR, } ``` A macro reads its options from `args_src`. The helpers in `macroforge_ts::builtin::derive::common` parse the common shapes: Rust ``` use macroforge_ts::builtin::derive::common::{extract_named_string, has_flag}; for decorator in &field.decorators {     if decorator.name.eq_ignore_ascii_case("validate") {         let required = has_flag(&decorator.args_src, "required");         // rename: "id", rename = "id" or rename("id"); escapes are decoded         let rename = extract_named_string(&decorator.args_src, "rename");     } } ``` ## Spans Every IR node carries a `SpanIR`. Spans count positions, which are byte offsets plus one; see [Spans](../../docs/custom-macros/output#spans) for reading the text a span covers. ## Next Steps - [Type-Aware Macros](../../docs/custom-macros/type-aware) - [Output and Imports](../../docs/custom-macros/output)