@macroforge/mcp-server 0.1.42 → 0.1.49
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +22 -0
- package/docs/BOOK.md +165 -0
- package/docs/api/api-overview.md +67 -48
- package/docs/api/expand-sync.md +88 -53
- package/docs/api/native-plugin.md +121 -71
- package/docs/api/position-mapper.md +115 -55
- package/docs/api/transform-sync.md +86 -60
- package/docs/builtin-macros/clone.md +0 -20
- package/docs/builtin-macros/debug.md +0 -23
- package/docs/builtin-macros/default.md +1 -40
- package/docs/builtin-macros/deserialize/example.md +8 -1416
- package/docs/builtin-macros/deserialize.md +8 -1416
- package/docs/builtin-macros/hash.md +0 -42
- package/docs/builtin-macros/macros-overview/detailed-documentation.md +13 -0
- package/docs/builtin-macros/macros-overview/enum-support.md +30 -0
- package/docs/builtin-macros/macros-overview/interface-support.md +28 -0
- package/docs/builtin-macros/macros-overview/overview.md +36 -0
- package/docs/builtin-macros/macros-overview/type-alias-support.md +62 -0
- package/docs/builtin-macros/macros-overview.md +171 -130
- package/docs/builtin-macros/ord.md +0 -25
- package/docs/builtin-macros/partial-eq.md +0 -84
- package/docs/builtin-macros/partial-ord.md +2 -32
- package/docs/builtin-macros/serialize.md +2 -62
- package/docs/concepts/architecture.md +125 -48
- package/docs/concepts/derive-system/built-in-vs-custom-macros.md +13 -0
- package/docs/concepts/derive-system/overview.md +200 -0
- package/docs/concepts/derive-system.md +157 -104
- package/docs/concepts/how-macros-work.md +98 -47
- package/docs/custom-macros/custom-overview.md +79 -57
- package/docs/custom-macros/rust-setup.md +138 -99
- package/docs/custom-macros/ts-macro-derive/accessing-field-data.md +40 -31
- package/docs/custom-macros/ts-macro-derive/adding-imports.md +14 -11
- package/docs/custom-macros/ts-macro-derive/attribute-options.md +20 -25
- package/docs/custom-macros/ts-macro-derive/complete-example.md +40 -38
- package/docs/custom-macros/ts-macro-derive/deriveinput-structure.md +49 -47
- package/docs/custom-macros/ts-macro-derive/function-signature.md +12 -0
- package/docs/custom-macros/ts-macro-derive/overview.md +9 -7
- package/docs/custom-macros/ts-macro-derive/parsing-input.md +20 -18
- package/docs/custom-macros/ts-macro-derive/returning-errors.md +15 -13
- package/docs/custom-macros/ts-macro-derive.md +322 -228
- package/docs/custom-macros/ts-quote/backtick-template-literals.md +19 -7
- package/docs/custom-macros/ts-quote/comments-and.md +56 -22
- package/docs/custom-macros/ts-quote/complete-example-json-derive-macro.md +89 -98
- package/docs/custom-macros/ts-quote/conditionals-ifif.md +35 -29
- package/docs/custom-macros/ts-quote/identifier-concatenation-content.md +30 -22
- package/docs/custom-macros/ts-quote/iteration-for.md +48 -40
- package/docs/custom-macros/ts-quote/local-constants-let.md +23 -21
- package/docs/custom-macros/ts-quote/match-expressions-match.md +46 -38
- package/docs/custom-macros/ts-quote/overview.md +5 -10
- package/docs/custom-macros/ts-quote/pattern-matching-iflet.md +39 -0
- package/docs/custom-macros/ts-quote/quick-reference.md +50 -129
- package/docs/custom-macros/ts-quote/side-effects-do.md +13 -78
- package/docs/custom-macros/ts-quote/string-interpolation-textexpr.md +36 -0
- package/docs/custom-macros/ts-quote/tsstream-injection-typescript.md +43 -35
- package/docs/custom-macros/ts-quote/while-loops-while.md +31 -23
- package/docs/custom-macros/ts-quote.md +800 -520
- package/docs/getting-started/first-macro.md +98 -71
- package/docs/getting-started/installation.md +109 -65
- package/docs/integration/cli.md +214 -105
- package/docs/integration/configuration.md +115 -72
- package/docs/integration/integration-overview.md +55 -18
- package/docs/integration/mcp-server.md +84 -43
- package/docs/integration/svelte-preprocessor.md +183 -126
- package/docs/integration/typescript-plugin.md +101 -53
- package/docs/integration/vite-plugin.md +116 -76
- package/docs/language-servers/ls-overview.md +37 -21
- package/docs/language-servers/svelte.md +69 -38
- package/docs/language-servers/zed.md +81 -44
- package/docs/roadmap/roadmap.md +75 -53
- package/docs/sections.json +333 -44
- package/package.json +27 -28
|
@@ -90,48 +90,6 @@ export function userHashCode(value: User): number {
|
|
|
90
90
|
}
|
|
91
91
|
```
|
|
92
92
|
|
|
93
|
-
Generated output:
|
|
94
|
-
|
|
95
|
-
```typescript
|
|
96
|
-
class User {
|
|
97
|
-
id: number;
|
|
98
|
-
name: string;
|
|
99
|
-
|
|
100
|
-
cachedScore: number;
|
|
101
|
-
|
|
102
|
-
static equals(a: User, b: User): boolean {
|
|
103
|
-
return userEquals(a, b);
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
static hashCode(value: User): number {
|
|
107
|
-
return userHashCode(value);
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
export function userEquals(a: User, b: User): boolean {
|
|
112
|
-
if (a === b) return true;
|
|
113
|
-
return a.id === b.id && a.name === b.name;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
export function userHashCode(value: User): number {
|
|
117
|
-
let hash = 17;
|
|
118
|
-
hash =
|
|
119
|
-
(hash * 31 +
|
|
120
|
-
(Number.isInteger(value.id)
|
|
121
|
-
? value.id | 0
|
|
122
|
-
: value.id
|
|
123
|
-
.toString()
|
|
124
|
-
.split('')
|
|
125
|
-
.reduce((h, c) => (h * 31 + c.charCodeAt(0)) | 0, 0))) |
|
|
126
|
-
0;
|
|
127
|
-
hash =
|
|
128
|
-
(hash * 31 +
|
|
129
|
-
(value.name ?? '').split('').reduce((h, c) => (h * 31 + c.charCodeAt(0)) | 0, 0)) |
|
|
130
|
-
0;
|
|
131
|
-
return hash;
|
|
132
|
-
}
|
|
133
|
-
```
|
|
134
|
-
|
|
135
93
|
## Equality Contract
|
|
136
94
|
|
|
137
95
|
When implementing `PartialEq`, consider also implementing `Hash`:
|
|
@@ -192,45 +150,3 @@ export function userHashCode(value: User): number {
|
|
|
192
150
|
return hash;
|
|
193
151
|
}
|
|
194
152
|
```
|
|
195
|
-
|
|
196
|
-
Generated output:
|
|
197
|
-
|
|
198
|
-
```typescript
|
|
199
|
-
class User {
|
|
200
|
-
id: number;
|
|
201
|
-
name: string;
|
|
202
|
-
|
|
203
|
-
cachedScore: number;
|
|
204
|
-
|
|
205
|
-
static equals(a: User, b: User): boolean {
|
|
206
|
-
return userEquals(a, b);
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
static hashCode(value: User): number {
|
|
210
|
-
return userHashCode(value);
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
export function userEquals(a: User, b: User): boolean {
|
|
215
|
-
if (a === b) return true;
|
|
216
|
-
return a.id === b.id && a.name === b.name;
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
export function userHashCode(value: User): number {
|
|
220
|
-
let hash = 17;
|
|
221
|
-
hash =
|
|
222
|
-
(hash * 31 +
|
|
223
|
-
(Number.isInteger(value.id)
|
|
224
|
-
? value.id | 0
|
|
225
|
-
: value.id
|
|
226
|
-
.toString()
|
|
227
|
-
.split('')
|
|
228
|
-
.reduce((h, c) => (h * 31 + c.charCodeAt(0)) | 0, 0))) |
|
|
229
|
-
0;
|
|
230
|
-
hash =
|
|
231
|
-
(hash * 31 +
|
|
232
|
-
(value.name ?? '').split('').reduce((h, c) => (h * 31 + c.charCodeAt(0)) | 0, 0)) |
|
|
233
|
-
0;
|
|
234
|
-
return hash;
|
|
235
|
-
}
|
|
236
|
-
```
|
|
@@ -97,36 +97,6 @@ export function temperaturePartialCompare(a: Temperature, b: Temperature): numbe
|
|
|
97
97
|
}
|
|
98
98
|
```
|
|
99
99
|
|
|
100
|
-
|
|
100
|
+
## Return Type
|
|
101
101
|
|
|
102
|
-
|
|
103
|
-
class Temperature {
|
|
104
|
-
value: number | null;
|
|
105
|
-
unit: string;
|
|
106
|
-
|
|
107
|
-
static compareTo(a: Temperature, b: Temperature): number | null {
|
|
108
|
-
return temperaturePartialCompare(a, b);
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
export function temperaturePartialCompare(a: Temperature, b: Temperature): number | null {
|
|
113
|
-
if (a === b) return 0;
|
|
114
|
-
const cmp0 = (() => {
|
|
115
|
-
if (typeof (a.value as any)?.compareTo === 'function') {
|
|
116
|
-
const optResult = (a.value as any).compareTo(b.value);
|
|
117
|
-
return optResult === null ? null : optResult;
|
|
118
|
-
}
|
|
119
|
-
return a.value === b.value ? 0 : null;
|
|
120
|
-
})();
|
|
121
|
-
if (cmp0 === null) return null;
|
|
122
|
-
if (cmp0 !== 0) return cmp0;
|
|
123
|
-
const cmp1 = a.unit.localeCompare(b.unit);
|
|
124
|
-
if (cmp1 === null) return null;
|
|
125
|
-
if (cmp1 !== 0) return cmp1;
|
|
126
|
-
return 0;
|
|
127
|
-
}
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
## Required Import
|
|
131
|
-
|
|
132
|
-
The generated code automatically adds an import for `Option` from `macroforge/utils`.
|
|
102
|
+
The generated functions return `number | null` where `null` indicates incomparable values.
|
|
@@ -94,7 +94,7 @@ class User {
|
|
|
94
94
|
@param value - The value to serialize
|
|
95
95
|
@param ctx - The serialization context */
|
|
96
96
|
|
|
97
|
-
static serializeWithContext(value: User, ctx:
|
|
97
|
+
static serializeWithContext(value: User, ctx: @{SERIALIZE_CONTEXT}): Record<string, unknown> {
|
|
98
98
|
return userSerializeWithContext(value, ctx);
|
|
99
99
|
}
|
|
100
100
|
}
|
|
@@ -104,67 +104,7 @@ class User {
|
|
|
104
104
|
@returns JSON string representation with cycle detection metadata */ export function userSerialize(
|
|
105
105
|
value: User
|
|
106
106
|
): string {
|
|
107
|
-
const ctx =
|
|
108
|
-
return JSON.stringify(userSerializeWithContext(value, ctx));
|
|
109
|
-
} /** @internal Serializes with an existing context for nested/cyclic object graphs.
|
|
110
|
-
@param value - The value to serialize
|
|
111
|
-
@param ctx - The serialization context */
|
|
112
|
-
export function userSerializeWithContext(
|
|
113
|
-
value: User,
|
|
114
|
-
ctx: SerializeContext
|
|
115
|
-
): Record<string, unknown> {
|
|
116
|
-
const existingId = ctx.getId(value);
|
|
117
|
-
if (existingId !== undefined) {
|
|
118
|
-
return { __ref: existingId };
|
|
119
|
-
}
|
|
120
|
-
const __id = ctx.register(value);
|
|
121
|
-
const result: Record<string, unknown> = { __type: 'User', __id };
|
|
122
|
-
result['id'] = value.id;
|
|
123
|
-
result['userName'] = value.name;
|
|
124
|
-
{
|
|
125
|
-
const __flattened = userMetadataSerializeWithContext(value.metadata, ctx);
|
|
126
|
-
const { __type: _, __id: __, ...rest } = __flattened as any;
|
|
127
|
-
Object.assign(result, rest);
|
|
128
|
-
}
|
|
129
|
-
return result;
|
|
130
|
-
}
|
|
131
|
-
```
|
|
132
|
-
|
|
133
|
-
Generated output:
|
|
134
|
-
|
|
135
|
-
```typescript
|
|
136
|
-
import { SerializeContext } from 'macroforge/serde';
|
|
137
|
-
|
|
138
|
-
class User {
|
|
139
|
-
id: number;
|
|
140
|
-
|
|
141
|
-
name: string;
|
|
142
|
-
|
|
143
|
-
password: string;
|
|
144
|
-
|
|
145
|
-
metadata: UserMetadata;
|
|
146
|
-
/** Serializes a value to a JSON string.
|
|
147
|
-
@param value - The value to serialize
|
|
148
|
-
@returns JSON string representation with cycle detection metadata */
|
|
149
|
-
|
|
150
|
-
static serialize(value: User): string {
|
|
151
|
-
return userSerialize(value);
|
|
152
|
-
}
|
|
153
|
-
/** @internal Serializes with an existing context for nested/cyclic object graphs.
|
|
154
|
-
@param value - The value to serialize
|
|
155
|
-
@param ctx - The serialization context */
|
|
156
|
-
|
|
157
|
-
static serializeWithContext(value: User, ctx: SerializeContext): Record<string, unknown> {
|
|
158
|
-
return userSerializeWithContext(value, ctx);
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
/** Serializes a value to a JSON string.
|
|
163
|
-
@param value - The value to serialize
|
|
164
|
-
@returns JSON string representation with cycle detection metadata */ export function userSerialize(
|
|
165
|
-
value: User
|
|
166
|
-
): string {
|
|
167
|
-
const ctx = SerializeContext.create();
|
|
107
|
+
const ctx = @{SERIALIZE_CONTEXT}.create();
|
|
168
108
|
return JSON.stringify(userSerializeWithContext(value, ctx));
|
|
169
109
|
} /** @internal Serializes with an existing context for nested/cyclic object graphs.
|
|
170
110
|
@param value - The value to serialize
|
|
@@ -1,49 +1,126 @@
|
|
|
1
1
|
# Architecture
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
2
|
+
|
|
3
|
+
Macroforge is built as a native Node.js module using Rust and NAPI-RS. It leverages SWC for fast TypeScript parsing and code generation.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
Node.js / Vite
|
|
8
|
+
|
|
9
|
+
NAPI-RS Bindings
|
|
10
|
+
|
|
11
|
+
Macro Crates
|
|
12
|
+
|
|
13
|
+
macroforge\_ts\_syn
|
|
14
|
+
|
|
15
|
+
macroforge\_ts\_quote
|
|
16
|
+
|
|
17
|
+
macroforge\_ts\_macros
|
|
18
|
+
|
|
19
|
+
SWC Core
|
|
20
|
+
|
|
21
|
+
TypeScript parsing & codegen
|
|
22
|
+
|
|
23
|
+
## Core Components
|
|
24
|
+
|
|
25
|
+
### SWC Core
|
|
26
|
+
|
|
27
|
+
The foundation layer provides:
|
|
28
|
+
|
|
29
|
+
* Fast TypeScript/JavaScript parsing
|
|
30
|
+
* AST representation
|
|
31
|
+
* Code generation (AST → source code)
|
|
32
|
+
|
|
33
|
+
### macroforge\_ts\_syn
|
|
34
|
+
|
|
35
|
+
A Rust crate that provides:
|
|
36
|
+
|
|
37
|
+
* TypeScript-specific AST types
|
|
38
|
+
* Parsing utilities for macro input
|
|
39
|
+
* Derive input structures (class fields, decorators, etc.)
|
|
40
|
+
|
|
41
|
+
### macroforge\_ts\_quote
|
|
42
|
+
|
|
43
|
+
Template-based code generation similar to Rust's `quote!`:
|
|
44
|
+
|
|
45
|
+
* `ts_template!` - Generate TypeScript code from templates
|
|
46
|
+
* `body!` - Generate class body members
|
|
47
|
+
* Control flow: `{"{#for}"}`, `{"{#if}"}`, `{"{$let}"}`
|
|
48
|
+
|
|
49
|
+
### macroforge\_ts\_macros
|
|
50
|
+
|
|
51
|
+
The procedural macro attribute for defining derive macros:
|
|
52
|
+
|
|
53
|
+
* `#[ts_macro_derive(Name)]` attribute
|
|
54
|
+
* Automatic registration with the macro system
|
|
55
|
+
* Error handling and span tracking
|
|
56
|
+
|
|
57
|
+
### NAPI-RS Bindings
|
|
58
|
+
|
|
59
|
+
Bridges Rust and Node.js:
|
|
60
|
+
|
|
61
|
+
* Exposes `expandSync`, `transformSync`, etc.
|
|
62
|
+
* Provides the `NativePlugin` class for caching
|
|
63
|
+
* Handles data marshaling between Rust and JavaScript
|
|
64
|
+
|
|
65
|
+
## Data Flow
|
|
66
|
+
|
|
67
|
+
1\. Source Code
|
|
68
|
+
|
|
69
|
+
TypeScript with @derive
|
|
70
|
+
|
|
71
|
+
2\. NAPI-RS
|
|
72
|
+
|
|
73
|
+
receives JavaScript string
|
|
74
|
+
|
|
75
|
+
3\. SWC Parser
|
|
76
|
+
|
|
77
|
+
parses to AST
|
|
78
|
+
|
|
79
|
+
4\. Macro Expander
|
|
80
|
+
|
|
81
|
+
finds @derive decorators
|
|
82
|
+
|
|
83
|
+
5\. For Each Macro
|
|
84
|
+
|
|
85
|
+
extract data, run macro, generate AST nodes
|
|
86
|
+
|
|
87
|
+
6\. Merge
|
|
88
|
+
|
|
89
|
+
generated nodes into AST
|
|
90
|
+
|
|
91
|
+
7\. SWC Codegen
|
|
92
|
+
|
|
93
|
+
generates source code
|
|
94
|
+
|
|
95
|
+
8\. Return
|
|
96
|
+
|
|
97
|
+
to JavaScript with source mapping
|
|
98
|
+
|
|
99
|
+
## Performance Characteristics
|
|
100
|
+
|
|
101
|
+
* **Thread-safe**: Each expansion runs in an isolated thread with a 32MB stack
|
|
102
|
+
* **Caching**: `NativePlugin` caches results by file version
|
|
103
|
+
* **Binary search**: Position mapping uses O(log n) lookups
|
|
104
|
+
* **Zero-copy**: SWC's arena allocator minimizes allocations
|
|
105
|
+
|
|
106
|
+
## Re-exported Crates
|
|
107
|
+
|
|
108
|
+
For custom macro development, `macroforge_ts` re-exports everything you need:
|
|
109
|
+
|
|
110
|
+
Rust
|
|
111
|
+
|
|
112
|
+
```
|
|
113
|
+
// Convenient re-exports for macro development
|
|
114
|
+
use macroforge_ts::macros::{ts_macro_derive, body, ts_template, above, below, signature};
|
|
115
|
+
use macroforge_ts::ts_syn::{Data, DeriveInput, MacroforgeError, TsStream, parse_ts_macro_input};
|
|
116
|
+
|
|
117
|
+
// Also available: raw crate access and SWC modules
|
|
118
|
+
use macroforge_ts::swc_core;
|
|
119
|
+
use macroforge_ts::swc_common;
|
|
120
|
+
use macroforge_ts::swc_ecma_ast;
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## Next Steps
|
|
124
|
+
|
|
125
|
+
* [Write custom macros](../../docs/custom-macros)
|
|
126
|
+
* [Explore the API reference](../../docs/api)
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
## Built-in vs Custom Macros
|
|
2
|
+
|
|
3
|
+
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.
|
|
4
|
+
|
|
5
|
+
| Type | Import Required | Examples |
|
|
6
|
+
| -------- | --------------- | ------------------------------------------------------------------------------- |
|
|
7
|
+
| Built-in | No | Debug, Clone, Default, Hash, Ord, PartialEq, PartialOrd, Serialize, Deserialize |
|
|
8
|
+
| Custom | Yes | Any macro from an external package |
|
|
9
|
+
|
|
10
|
+
## Next Steps
|
|
11
|
+
|
|
12
|
+
* [Explore built-in macros](../../docs/builtin-macros)
|
|
13
|
+
* [Create custom macros](../../docs/custom-macros)
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
# The Derive System
|
|
2
|
+
|
|
3
|
+
The derive system is inspired by Rust's derive macros. It allows you to automatically implement common patterns by annotating your classes with `@derive`.
|
|
4
|
+
|
|
5
|
+
## Syntax Reference
|
|
6
|
+
|
|
7
|
+
Macroforge uses JSDoc comments for all macro annotations. This ensures compatibility with standard TypeScript tooling.
|
|
8
|
+
|
|
9
|
+
### The @derive Statement
|
|
10
|
+
|
|
11
|
+
The `@derive` decorator triggers macro expansion on a class or interface:
|
|
12
|
+
|
|
13
|
+
Source
|
|
14
|
+
|
|
15
|
+
TypeScript
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
/** @derive(Debug) */
|
|
19
|
+
class MyClass {
|
|
20
|
+
value: string;
|
|
21
|
+
}
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Syntax rules:
|
|
25
|
+
|
|
26
|
+
* Must be inside a JSDoc comment (`/** */`)
|
|
27
|
+
* Must appear immediately before the class/interface declaration
|
|
28
|
+
* Multiple macros can be comma-separated: `@derive(A, B, C)`
|
|
29
|
+
* Multiple `@derive` statements can be stacked
|
|
30
|
+
|
|
31
|
+
Source
|
|
32
|
+
|
|
33
|
+
TypeScript
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
/** @derive(Debug, Clone) */
|
|
37
|
+
class User {
|
|
38
|
+
name: string;
|
|
39
|
+
email: string;
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### The import macro Statement
|
|
44
|
+
|
|
45
|
+
To use macros from external packages, you must declare them with `import macro`:
|
|
46
|
+
|
|
47
|
+
TypeScript
|
|
48
|
+
|
|
49
|
+
```
|
|
50
|
+
/** import macro { MacroName } from "package-name"; */
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Syntax rules:
|
|
54
|
+
|
|
55
|
+
* Must be inside a JSDoc comment (`/** */`)
|
|
56
|
+
* Can appear anywhere in the file (typically at the top)
|
|
57
|
+
* Multiple macros can be imported: `import macro { A, B } from "pkg";`
|
|
58
|
+
* Multiple import statements can be used for different packages
|
|
59
|
+
|
|
60
|
+
TypeScript
|
|
61
|
+
|
|
62
|
+
```
|
|
63
|
+
/** import macro { JSON, Validate } from "@my/macros"; */
|
|
64
|
+
/** import macro { Builder } from "@other/macros"; */
|
|
65
|
+
|
|
66
|
+
/** @derive(JSON, Validate, Builder) */
|
|
67
|
+
class User {
|
|
68
|
+
name: string;
|
|
69
|
+
email: string;
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Built-in macros
|
|
74
|
+
|
|
75
|
+
Built-in macros (Debug, Clone, Default, Hash, Ord, PartialEq, PartialOrd, Serialize, Deserialize) do not require an import statement.
|
|
76
|
+
|
|
77
|
+
### Field Attributes
|
|
78
|
+
|
|
79
|
+
Macros can define field-level attributes to customize behavior per field:
|
|
80
|
+
|
|
81
|
+
Before (Your Code)
|
|
82
|
+
|
|
83
|
+
```
|
|
84
|
+
/** @derive(Debug, Serialize) */
|
|
85
|
+
class User {
|
|
86
|
+
/** @debug({ rename: "userId" }) */
|
|
87
|
+
/** @serde({ rename: "user_id" }) */
|
|
88
|
+
id: number;
|
|
89
|
+
|
|
90
|
+
name: string;
|
|
91
|
+
|
|
92
|
+
/** @debug({ skip: true }) */
|
|
93
|
+
/** @serde({ skip: true }) */
|
|
94
|
+
password: string;
|
|
95
|
+
|
|
96
|
+
metadata: Record<string, unknown>;
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
After (Generated)
|
|
101
|
+
|
|
102
|
+
```
|
|
103
|
+
import { SerializeContext as __mf_SerializeContext } from 'macroforge/serde';
|
|
104
|
+
|
|
105
|
+
class User {
|
|
106
|
+
id: number;
|
|
107
|
+
|
|
108
|
+
name: string;
|
|
109
|
+
|
|
110
|
+
password: string;
|
|
111
|
+
|
|
112
|
+
metadata: Record<string, unknown>;
|
|
113
|
+
|
|
114
|
+
static toString(value: User): string {
|
|
115
|
+
return userToString(value);
|
|
116
|
+
}
|
|
117
|
+
/** Serializes a value to a JSON string.
|
|
118
|
+
@param value - The value to serialize
|
|
119
|
+
@returns JSON string representation with cycle detection metadata */
|
|
120
|
+
|
|
121
|
+
static serialize(value: User): string {
|
|
122
|
+
return userSerialize(value);
|
|
123
|
+
}
|
|
124
|
+
/** @internal Serializes with an existing context for nested/cyclic object graphs.
|
|
125
|
+
@param value - The value to serialize
|
|
126
|
+
@param ctx - The serialization context */
|
|
127
|
+
|
|
128
|
+
static serializeWithContext(value: User, ctx: __mf_SerializeContext): Record<string, unknown> {
|
|
129
|
+
return userSerializeWithContext(value, ctx);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export function userToString(value: User): string {
|
|
134
|
+
const parts: string[] = [];
|
|
135
|
+
parts.push('userId: ' + value.id);
|
|
136
|
+
parts.push('name: ' + value.name);
|
|
137
|
+
parts.push('metadata: ' + value.metadata);
|
|
138
|
+
return 'User { ' + parts.join(', ') + ' }';
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/** Serializes a value to a JSON string.
|
|
142
|
+
@param value - The value to serialize
|
|
143
|
+
@returns JSON string representation with cycle detection metadata */ export function userSerialize(
|
|
144
|
+
value: User
|
|
145
|
+
): string {
|
|
146
|
+
const ctx = __mf_SerializeContext.create();
|
|
147
|
+
return JSON.stringify(userSerializeWithContext(value, ctx));
|
|
148
|
+
} /** @internal Serializes with an existing context for nested/cyclic object graphs.
|
|
149
|
+
@param value - The value to serialize
|
|
150
|
+
@param ctx - The serialization context */
|
|
151
|
+
export function userSerializeWithContext(
|
|
152
|
+
value: User,
|
|
153
|
+
ctx: __mf_SerializeContext
|
|
154
|
+
): Record<string, unknown> {
|
|
155
|
+
const existingId = ctx.getId(value);
|
|
156
|
+
if (existingId !== undefined) {
|
|
157
|
+
return { __ref: existingId };
|
|
158
|
+
}
|
|
159
|
+
const __id = ctx.register(value);
|
|
160
|
+
const result: Record<string, unknown> = { __type: 'User', __id };
|
|
161
|
+
result['user_id'] = value.id;
|
|
162
|
+
result['name'] = value.name;
|
|
163
|
+
result['metadata'] = value.metadata;
|
|
164
|
+
return result;
|
|
165
|
+
}
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
Syntax rules:
|
|
169
|
+
|
|
170
|
+
* Must be inside a JSDoc comment immediately before the field
|
|
171
|
+
* Options use object literal syntax: `@attr({ key: value })`
|
|
172
|
+
* Boolean options: `@attr({ skip: true })`
|
|
173
|
+
* String options: `@attr({ rename: "newName" })`
|
|
174
|
+
* Multiple attributes can be on separate lines or combined
|
|
175
|
+
|
|
176
|
+
Common field attributes by macro:
|
|
177
|
+
|
|
178
|
+
| Macro | Attribute | Options |
|
|
179
|
+
| --------------------- | ------------- | -------------------------------------- |
|
|
180
|
+
| Debug | `@debug` | `skip`, `rename` |
|
|
181
|
+
| Clone | `@clone` | `skip`, `clone_with` |
|
|
182
|
+
| Serialize/Deserialize | `@serde` | `skip`, `rename`, `flatten`, `default` |
|
|
183
|
+
| Hash | `@hash` | `skip` |
|
|
184
|
+
| PartialEq/Ord | `@eq`, `@ord` | `skip` |
|
|
185
|
+
|
|
186
|
+
## How It Works
|
|
187
|
+
|
|
188
|
+
1. **Declaration**: You write `@derive(MacroName)` before a class
|
|
189
|
+
2. **Discovery**: Macroforge finds all derive decorators in your code
|
|
190
|
+
3. **Expansion**: Each named macro receives the class AST and generates code
|
|
191
|
+
4. **Injection**: Generated methods/properties are added to the class
|
|
192
|
+
|
|
193
|
+
## What Can Be Derived
|
|
194
|
+
|
|
195
|
+
The derive system works on:
|
|
196
|
+
|
|
197
|
+
* **Classes**: The primary target for derive macros
|
|
198
|
+
* **Interfaces**: Macros generate companion namespace functions
|
|
199
|
+
* **Enums**: Macros generate namespace functions for enum values
|
|
200
|
+
* **Type aliases**: Both object types and union types are supported
|