@macroforge/mcp-server 0.1.39 → 0.1.42
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/docs/api/api-overview.md +32 -32
- package/docs/api/expand-sync.md +30 -30
- package/docs/api/native-plugin.md +38 -38
- package/docs/api/position-mapper.md +18 -18
- package/docs/api/transform-sync.md +31 -31
- package/docs/builtin-macros/default.md +6 -6
- package/docs/builtin-macros/macros-overview.md +84 -84
- package/docs/builtin-macros/partial-ord.md +18 -20
- package/docs/concepts/architecture.md +16 -16
- package/docs/concepts/derive-system.md +89 -68
- package/docs/concepts/how-macros-work.md +13 -12
- package/docs/custom-macros/custom-overview.md +36 -36
- package/docs/custom-macros/rust-setup.md +71 -71
- package/docs/custom-macros/ts-macro-derive.md +167 -167
- package/docs/custom-macros/ts-quote.md +347 -347
- package/docs/getting-started/first-macro.md +57 -55
- package/docs/getting-started/installation.md +34 -35
- package/docs/integration/cli.md +43 -43
- package/docs/integration/configuration.md +41 -41
- package/docs/integration/integration-overview.md +4 -4
- package/docs/integration/mcp-server.md +22 -22
- package/docs/integration/svelte-preprocessor.md +87 -87
- package/docs/integration/typescript-plugin.md +23 -24
- package/docs/integration/vite-plugin.md +40 -40
- package/docs/language-servers/svelte.md +15 -16
- package/docs/language-servers/zed.md +14 -15
- package/package.json +2 -2
|
@@ -1,242 +1,242 @@
|
|
|
1
1
|
# ts_macro_derive
|
|
2
|
-
*The
|
|
2
|
+
*The <code class="shiki-inline"><span class="line"><span style="--shiki-dark:#E1E4E8;--shiki-light:#24292E">#[ts_macro_derive]</code> attribute is a Rust procedural macro that registers your function as a Macroforge derive macro.*
|
|
3
3
|
## Basic Syntax
|
|
4
4
|
```
|
|
5
|
-
use
|
|
6
|
-
use
|
|
5
|
+
use macroforge_ts::macros::ts_macro_derive;
|
|
6
|
+
use macroforge_ts::ts_syn::{TsStream, MacroforgeError};
|
|
7
7
|
|
|
8
8
|
#[ts_macro_derive(MacroName)]
|
|
9
|
-
pub
|
|
10
|
-
|
|
9
|
+
pub fn my_macro(mut input: TsStream) -> Result<TsStream, MacroforgeError> {
|
|
10
|
+
// Macro implementation
|
|
11
11
|
}
|
|
12
12
|
``` ## Attribute Options
|
|
13
13
|
### Name (Required)
|
|
14
|
-
The first argument is the macro name that users will reference in
|
|
14
|
+
The first argument is the macro name that users will reference in <code class="shiki-inline"><span class="line"><span style="--shiki-dark:#E1E4E8;--shiki-light:#24292E">@<span style="--shiki-dark:#B392F0;--shiki-light:#6F42C1">derive<span style="--shiki-dark:#E1E4E8;--shiki-light:#24292E">()</code>:
|
|
15
15
|
```
|
|
16
|
-
#[ts_macro_derive(JSON)]
|
|
17
|
-
pub
|
|
16
|
+
#[ts_macro_derive(JSON)] // Users write: @derive(JSON)
|
|
17
|
+
pub fn derive_json(...)
|
|
18
18
|
``` ### Description
|
|
19
19
|
Provides documentation for the macro:
|
|
20
20
|
```
|
|
21
21
|
#[ts_macro_derive(
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
JSON,
|
|
23
|
+
description = "Generates toJSON() returning a plain object"
|
|
24
24
|
)]
|
|
25
|
-
pub
|
|
25
|
+
pub fn derive_json(...)
|
|
26
26
|
``` ### Attributes
|
|
27
27
|
Declare which field-level decorators your macro accepts:
|
|
28
28
|
```
|
|
29
29
|
#[ts_macro_derive(
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
30
|
+
Debug,
|
|
31
|
+
description = "Generates toString()",
|
|
32
|
+
attributes(debug) // Allows @debug({ ... }) on fields
|
|
33
33
|
)]
|
|
34
|
-
pub
|
|
34
|
+
pub fn derive_debug(...)
|
|
35
35
|
``` > **Note:** Declared attributes become available as @attributeName({ options }) decorators in TypeScript. ## Function Signature
|
|
36
36
|
```
|
|
37
|
-
pub
|
|
37
|
+
pub fn my_macro(mut input: TsStream) -> Result<TsStream, MacroforgeError>
|
|
38
38
|
``` | Parameter | Description |
|
|
39
39
|
| --- | --- |
|
|
40
|
-
|
|
|
41
|
-
|
|
|
40
|
+
| input: TsStream | Token stream containing the class/interface AST |
|
|
41
|
+
| Result<TsStream, MacroforgeError> | Returns generated code or an error with source location |
|
|
42
42
|
## Parsing Input
|
|
43
|
-
Use
|
|
43
|
+
Use <code class="shiki-inline"><span class="line"><span style="--shiki-dark:#B392F0;--shiki-light:#6F42C1">parse_ts_macro_input!</code> to convert the token stream:
|
|
44
44
|
```
|
|
45
|
-
use
|
|
45
|
+
use macroforge_ts::ts_syn::{Data, DeriveInput, parse_ts_macro_input};
|
|
46
46
|
|
|
47
47
|
#[ts_macro_derive(MyMacro)]
|
|
48
|
-
pub
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
48
|
+
pub fn my_macro(mut input: TsStream) -> Result<TsStream, MacroforgeError> {
|
|
49
|
+
let input = parse_ts_macro_input!(input as DeriveInput);
|
|
50
|
+
|
|
51
|
+
// Access class data
|
|
52
|
+
match &input.data {
|
|
53
|
+
Data::Class(class) => {
|
|
54
|
+
let class_name = input.name();
|
|
55
|
+
let fields = class.fields();
|
|
56
|
+
// ...
|
|
57
|
+
}
|
|
58
|
+
Data::Interface(interface) => {
|
|
59
|
+
// Handle interfaces
|
|
60
|
+
}
|
|
61
|
+
Data::Enum(_) => {
|
|
62
|
+
// Handle enums (if supported)
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
65
|
}
|
|
66
66
|
``` ## DeriveInput Structure
|
|
67
67
|
```
|
|
68
|
-
struct
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
68
|
+
struct DeriveInput {
|
|
69
|
+
pub ident: Ident, // The type name
|
|
70
|
+
pub span: SpanIR, // Span of the type definition
|
|
71
|
+
pub attrs: Vec<Attribute>, // Decorators (excluding @derive)
|
|
72
|
+
pub data: Data, // The parsed type data
|
|
73
|
+
pub context: MacroContextIR, // Macro context with spans
|
|
74
|
+
|
|
75
|
+
// Helper methods
|
|
76
|
+
fn name(&self) -> &str; // Get the type name
|
|
77
|
+
fn decorator_span(&self) -> SpanIR; // Span of @derive decorator
|
|
78
|
+
fn as_class(&self) -> Option<&DataClass>;
|
|
79
|
+
fn as_interface(&self) -> Option<&DataInterface>;
|
|
80
|
+
fn as_enum(&self) -> Option<&DataEnum>;
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
-
enum
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
83
|
+
enum Data {
|
|
84
|
+
Class(DataClass),
|
|
85
|
+
Interface(DataInterface),
|
|
86
|
+
Enum(DataEnum),
|
|
87
|
+
TypeAlias(DataTypeAlias),
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
-
impl
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
90
|
+
impl DataClass {
|
|
91
|
+
fn fields(&self) -> &[FieldIR];
|
|
92
|
+
fn methods(&self) -> &[MethodSigIR];
|
|
93
|
+
fn field_names(&self) -> impl Iterator<Item = &str>;
|
|
94
|
+
fn field(&self, name: &str) -> Option<&FieldIR>;
|
|
95
|
+
fn body_span(&self) -> SpanIR; // For inserting code into class body
|
|
96
|
+
fn type_params(&self) -> &[String]; // Generic type parameters
|
|
97
|
+
fn heritage(&self) -> &[String]; // extends/implements clauses
|
|
98
|
+
fn is_abstract(&self) -> bool;
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
-
impl
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
101
|
+
impl DataInterface {
|
|
102
|
+
fn fields(&self) -> &[InterfaceFieldIR];
|
|
103
|
+
fn methods(&self) -> &[InterfaceMethodIR];
|
|
104
|
+
fn field_names(&self) -> impl Iterator<Item = &str>;
|
|
105
|
+
fn field(&self, name: &str) -> Option<&InterfaceFieldIR>;
|
|
106
|
+
fn body_span(&self) -> SpanIR;
|
|
107
|
+
fn type_params(&self) -> &[String];
|
|
108
|
+
fn heritage(&self) -> &[String]; // extends clauses
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
-
impl
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
111
|
+
impl DataEnum {
|
|
112
|
+
fn variants(&self) -> &[EnumVariantIR];
|
|
113
|
+
fn variant_names(&self) -> impl Iterator<Item = &str>;
|
|
114
|
+
fn variant(&self, name: &str) -> Option<&EnumVariantIR>;
|
|
115
115
|
}
|
|
116
116
|
|
|
117
|
-
impl
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
117
|
+
impl DataTypeAlias {
|
|
118
|
+
fn body(&self) -> &TypeBody;
|
|
119
|
+
fn type_params(&self) -> &[String];
|
|
120
|
+
fn is_union(&self) -> bool;
|
|
121
|
+
fn is_object(&self) -> bool;
|
|
122
|
+
fn as_union(&self) -> Option<&[TypeMember]>;
|
|
123
|
+
fn as_object(&self) -> Option<&[InterfaceFieldIR]>;
|
|
124
124
|
}
|
|
125
125
|
``` ## Accessing Field Data
|
|
126
126
|
### Class Fields (FieldIR)
|
|
127
127
|
```
|
|
128
|
-
struct
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
128
|
+
struct FieldIR {
|
|
129
|
+
pub name: String, // Field name
|
|
130
|
+
pub span: SpanIR, // Field span
|
|
131
|
+
pub ts_type: String, // TypeScript type annotation
|
|
132
|
+
pub optional: bool, // Whether field has ?
|
|
133
|
+
pub readonly: bool, // Whether field is readonly
|
|
134
|
+
pub visibility: Visibility, // Public, Protected, Private
|
|
135
|
+
pub decorators: Vec<DecoratorIR>, // Field decorators
|
|
136
136
|
}
|
|
137
137
|
``` ### Interface Fields (InterfaceFieldIR)
|
|
138
138
|
```
|
|
139
|
-
struct
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
139
|
+
struct InterfaceFieldIR {
|
|
140
|
+
pub name: String,
|
|
141
|
+
pub span: SpanIR,
|
|
142
|
+
pub ts_type: String,
|
|
143
|
+
pub optional: bool,
|
|
144
|
+
pub readonly: bool,
|
|
145
|
+
pub decorators: Vec<DecoratorIR>,
|
|
146
|
+
// Note: No visibility field (interfaces are always public)
|
|
147
147
|
}
|
|
148
148
|
``` ### Enum Variants (EnumVariantIR)
|
|
149
149
|
```
|
|
150
|
-
struct
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
150
|
+
struct EnumVariantIR {
|
|
151
|
+
pub name: String,
|
|
152
|
+
pub span: SpanIR,
|
|
153
|
+
pub value: EnumValue, // Auto, String(String), or Number(f64)
|
|
154
|
+
pub decorators: Vec<DecoratorIR>,
|
|
155
155
|
}
|
|
156
156
|
``` ### Decorator Structure
|
|
157
157
|
```
|
|
158
|
-
struct
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
158
|
+
struct DecoratorIR {
|
|
159
|
+
pub name: String, // e.g., "serde"
|
|
160
|
+
pub args_src: String, // Raw args text, e.g., "skip, rename: 'id'"
|
|
161
|
+
pub span: SpanIR,
|
|
162
162
|
}
|
|
163
163
|
``` > **Note:** To check for decorators, iterate through field.decorators and check decorator.name. For parsing options, you can write helper functions like the built-in macros do. ## Adding Imports
|
|
164
|
-
If your macro generates code that requires imports, use the
|
|
164
|
+
If your macro generates code that requires imports, use the <code class="shiki-inline"><span class="line"><span style="--shiki-dark:#E1E4E8;--shiki-light:#24292E">add_import</code> method on <code class="shiki-inline"><span class="line"><span style="--shiki-dark:#E1E4E8;--shiki-light:#24292E">TsStream</code>:
|
|
165
165
|
```
|
|
166
|
-
|
|
167
|
-
let
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
166
|
+
// Add an import to be inserted at the top of the file
|
|
167
|
+
let mut output = body! {
|
|
168
|
+
validate(): ValidationResult {
|
|
169
|
+
return validateFields(this);
|
|
170
|
+
}
|
|
171
171
|
};
|
|
172
172
|
|
|
173
|
-
|
|
174
|
-
output.add_import("validateFields"
|
|
175
|
-
output.add_import("ValidationResult"
|
|
173
|
+
// This will add: import { validateFields, ValidationResult } from "my-validation-lib";
|
|
174
|
+
output.add_import("validateFields", "my-validation-lib");
|
|
175
|
+
output.add_import("ValidationResult", "my-validation-lib");
|
|
176
176
|
|
|
177
177
|
Ok(output)
|
|
178
178
|
``` > **Note:** Imports are automatically deduplicated. If the same import already exists in the file, it won't be added again. ## Returning Errors
|
|
179
|
-
Use
|
|
179
|
+
Use <code class="shiki-inline"><span class="line"><span style="--shiki-dark:#E1E4E8;--shiki-light:#24292E">MacroforgeError</code> to report errors with source locations:
|
|
180
180
|
```
|
|
181
181
|
#[ts_macro_derive(ClassOnly)]
|
|
182
|
-
pub
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
182
|
+
pub fn class_only(mut input: TsStream) -> Result<TsStream, MacroforgeError> {
|
|
183
|
+
let input = parse_ts_macro_input!(input as DeriveInput);
|
|
184
|
+
|
|
185
|
+
match &input.data {
|
|
186
|
+
Data::Class(_) => {
|
|
187
|
+
// Generate code...
|
|
188
|
+
Ok(body! { /* ... */ })
|
|
189
|
+
}
|
|
190
|
+
_ => Err(MacroforgeError::new(
|
|
191
|
+
input.decorator_span(),
|
|
192
|
+
"@derive(ClassOnly) can only be used on classes",
|
|
193
|
+
)),
|
|
194
|
+
}
|
|
195
195
|
}
|
|
196
196
|
``` ## Complete Example
|
|
197
197
|
```
|
|
198
|
-
use
|
|
199
|
-
use
|
|
200
|
-
|
|
198
|
+
use macroforge_ts::macros::{ts_macro_derive, body};
|
|
199
|
+
use macroforge_ts::ts_syn::{
|
|
200
|
+
Data, DeriveInput, FieldIR, MacroforgeError, TsStream, parse_ts_macro_input,
|
|
201
201
|
};
|
|
202
202
|
|
|
203
|
-
|
|
204
|
-
fn
|
|
205
|
-
|
|
203
|
+
// Helper function to check if a field has a decorator
|
|
204
|
+
fn has_decorator(field: &FieldIR, name: &str) -> bool {
|
|
205
|
+
field.decorators.iter().any(|d| d.name.eq_ignore_ascii_case(name))
|
|
206
206
|
}
|
|
207
207
|
|
|
208
208
|
#[ts_macro_derive(
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
209
|
+
Validate,
|
|
210
|
+
description = "Generates a validate() method",
|
|
211
|
+
attributes(validate)
|
|
212
212
|
)]
|
|
213
|
-
pub
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
213
|
+
pub fn derive_validate(mut input: TsStream) -> Result<TsStream, MacroforgeError> {
|
|
214
|
+
let input = parse_ts_macro_input!(input as DeriveInput);
|
|
215
|
+
|
|
216
|
+
match &input.data {
|
|
217
|
+
Data::Class(class) => {
|
|
218
|
+
let validations: Vec<_> = class.fields()
|
|
219
|
+
.iter()
|
|
220
|
+
.filter(|f| has_decorator(f, "validate"))
|
|
221
|
+
.collect();
|
|
222
|
+
|
|
223
|
+
Ok(body! {
|
|
224
|
+
validate(): string[] {
|
|
225
|
+
const errors: string[] = [];
|
|
226
|
+
{#for field in validations}
|
|
227
|
+
if (!this.@{field.name}) {
|
|
228
|
+
errors.push("@{field.name} is required");
|
|
229
|
+
}
|
|
230
|
+
{/for}
|
|
231
|
+
return errors;
|
|
232
|
+
}
|
|
233
|
+
})
|
|
234
|
+
}
|
|
235
|
+
_ => Err(MacroforgeError::new(
|
|
236
|
+
input.decorator_span(),
|
|
237
|
+
"@derive(Validate) only works on classes",
|
|
238
|
+
)),
|
|
239
|
+
}
|
|
240
240
|
}
|
|
241
241
|
``` ## Next Steps
|
|
242
242
|
- [Learn the template syntax](../../docs/custom-macros/ts-quote)
|