# The Derive System

The derive system is inspired by Rust's derive macros. It allows you to automatically implement
common patterns by annotating your classes with `@derive`.

## Syntax Reference

Macroforge uses JSDoc comments for all macro annotations. This ensures compatibility with standard
TypeScript tooling.

### The @derive Statement

The `@derive` decorator triggers macro expansion on a class or interface:

Source

TypeScript

```
/** @derive(Debug) */
class MyClass {
  value: string;
}
```

Syntax rules:

- Must be inside a JSDoc comment (`/** */`)
- Must appear immediately before the class/interface declaration
- Multiple macros can be comma-separated: `@derive(A, B, C)`
- Multiple `@derive` statements can be stacked

Source

TypeScript

```
/** @derive(Debug, Clone) */
class User {
  name: string;
  email: string;
}
```

### The import macro Statement

To use macros from external packages, you must declare them with `import macro`:

TypeScript

```
/** import macro { MacroName } from "package-name"; */
```

Syntax rules:

- Must be inside a JSDoc comment (`/** */`)
- Can appear anywhere in the file (typically at the top)
- Multiple macros can be imported: `import macro { A, B } from "pkg";`
- Multiple import statements can be used for different packages

TypeScript

```
/** import macro { JSON, Validate } from "@my/macros"; */
/** import macro { Builder } from "@other/macros"; */

/** @derive(JSON, Validate, Builder) */
class User {
  name: string;
  email: string;
}
```

Built-in macros

Built-in macros (Debug, Clone, Default, Hash, Ord, PartialEq, PartialOrd, Serialize, Deserialize) do
not require an import statement.

### Field Attributes

Macros can define field-level attributes to customize behavior per field:

Before (Your Code)

```
/** @derive(Debug, Serialize) */
class User {
  /** @debug({ rename: "userId" }) */
  /** @serde({ rename: "user_id" }) */
  id: number;

  name: string;

  /** @debug({ skip: true }) */
  /** @serde({ skip: true }) */
  password: string;

  metadata: Record<string, unknown>;
}
```

After (Generated)

```
import { SerializeContext as __mf_SerializeContext } from "@macroforge/core/serde";

class User {
  
  
  id: number;

  name: string;

  
  
  password: string;

  metadata: Record<string, unknown>;

static toString(value: User): string {
                    return userToString(value);
                }
/** Serializes a value to a JSON string. @param value - The value to serialize @param keepMetadata - If true, preserves __type and __id fields in the output @returns JSON string representation  */
                static serialize(value: User, keepMetadata?: boolean): string {
                    return userSerialize(value, keepMetadata);
                }

                /** @internal Serializes with an existing context for nested/cyclic object graphs. @param value - The value to serialize @param ctx - The serialization context  */
                static serializeWithContext(value: User, ctx: __mf_SerializeContext): Record<string, unknown> {
                    return userSerializeWithContext(value, ctx);
                }}

export function userToString(value: User): string {
                        const parts: string[] = [];
                        parts.push("userId: " + value.id);
parts.push("name: " + value.name);
parts.push("metadata: " + value.metadata);

                        return "User { " + parts.join(", ") + " }";
                    }

/** Serializes a value to a JSON string. @param value - The value to serialize @param keepMetadata - If true, preserves __type and __id fields in the output @returns JSON string representation  */
                export function userSerialize(value: User, keepMetadata?: boolean): string {
                    const ctx = __mf_SerializeContext.create();
                    const __raw = userSerializeWithContext(value, ctx);
                    if (keepMetadata) return JSON.stringify(__raw);
                    return JSON.stringify(__raw, (key, val) => key === "__type" || key === "__id" ? undefined : val);
                }

                /** @internal Serializes with an existing context for nested/cyclic object graphs. @param value - The value to serialize @param ctx - The serialization context  */
                export function userSerializeWithContext(value: User, ctx: __mf_SerializeContext): Record<string, unknown> {

                    const existingId = ctx.getId(value);
                    if (existingId !== undefined) {
                        return { __ref: existingId };
                    }

                    const __id = ctx.register(value);

                    const result: Record<string, unknown> = {
                        "__type": "User",
                        __id,
                    };result.user_id = value.id;result.name = value.name;result.metadata = value.metadata;

                    return result;
                }
```

Syntax rules:

- Must be inside a JSDoc comment immediately before the field
- Options use object literal syntax: `@attr({ key: value })`
- Boolean options: `@attr({ skip: true })`
- String options: `@attr({ rename: "newName" })`
- Multiple attributes can be on separate lines or combined

Common field attributes by macro:

| Macro                 | Attribute     | Options                                |
| --------------------- | ------------- | -------------------------------------- |
| Debug                 | `@debug`      | `skip`, `rename`                       |
| Clone                 | `@clone`      | `skip`, `clone_with`                   |
| Serialize/Deserialize | `@serde`      | `skip`, `rename`, `flatten`, `default` |
| Hash                  | `@hash`       | `skip`                                 |
| PartialEq/Ord         | `@eq`, `@ord` | `skip`                                 |

## How It Works

1. **Declaration**: You write `@derive(MacroName)` before a class
2. **Discovery**: Macroforge finds all derive decorators in your code
3. **Expansion**: Each named macro receives the class AST and generates code
4. **Injection**: Generated methods/properties are added to the class

## What Can Be Derived

The derive system works on:

- **Classes**: The primary target for derive macros
- **Interfaces**: Macros generate companion namespace functions
- **Enums**: Macros generate namespace functions for enum values
- **Type aliases**: Both object types and union types are supported

## Built-in vs Custom Macros

Macroforge comes with built-in macros that work out of the box. You can also create custom macros in
Rust and use them via the `import macro` statement.

| Type     | Import Required | Examples                                                                        |
| -------- | --------------- | ------------------------------------------------------------------------------- |
| Built-in | No              | Debug, Clone, Default, Hash, Ord, PartialEq, PartialOrd, Serialize, Deserialize |
| Custom   | Yes             | Any macro from an external package                                              |

## Next Steps

- [Explore built-in macros](../../docs/builtin-macros)
- [Create custom macros](../../docs/custom-macros)
