@macroforge/mcp-server 0.1.42 → 0.1.50
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 +1 -242
- package/package.json +27 -28
|
@@ -1,242 +1,336 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
# ts\_macro\_derive
|
|
2
|
+
|
|
3
|
+
The `#[ts_macro_derive]` attribute is a Rust procedural macro that registers your function as a Macroforge derive macro.
|
|
4
|
+
|
|
5
|
+
## Basic Syntax
|
|
6
|
+
|
|
7
|
+
Rust
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
use macroforge_ts::macros::ts_macro_derive;
|
|
11
|
+
use macroforge_ts::ts_syn::{TsStream, MacroforgeError};
|
|
7
12
|
|
|
8
13
|
#[ts_macro_derive(MacroName)]
|
|
9
|
-
pub
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
```
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
14
|
+
pub fn my_macro(mut input: TsStream) -> Result<TsStream, MacroforgeError> {
|
|
15
|
+
// Macro implementation
|
|
16
|
+
}
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Attribute Options
|
|
20
|
+
|
|
21
|
+
### Name (Required)
|
|
22
|
+
|
|
23
|
+
The first argument is the macro name that users will reference in `@derive()`:
|
|
24
|
+
|
|
25
|
+
Rust
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
#[ts_macro_derive(JSON)] // Users write: @derive(JSON)
|
|
29
|
+
pub fn derive_json(...)
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### Description
|
|
33
|
+
|
|
34
|
+
Provides documentation for the macro:
|
|
35
|
+
|
|
36
|
+
Rust
|
|
37
|
+
|
|
38
|
+
```
|
|
21
39
|
#[ts_macro_derive(
|
|
22
|
-
|
|
23
|
-
|
|
40
|
+
JSON,
|
|
41
|
+
description = "Generates toJSON() returning a plain object"
|
|
24
42
|
)]
|
|
25
|
-
pub
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
|
|
43
|
+
pub fn derive_json(...)
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Attributes
|
|
47
|
+
|
|
48
|
+
Declare which field-level decorators your macro accepts:
|
|
49
|
+
|
|
50
|
+
Rust
|
|
51
|
+
|
|
52
|
+
```
|
|
29
53
|
#[ts_macro_derive(
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
54
|
+
Debug,
|
|
55
|
+
description = "Generates toString()",
|
|
56
|
+
attributes(debug) // Allows @debug({ ... }) on fields
|
|
33
57
|
)]
|
|
34
|
-
pub
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
58
|
+
pub fn derive_debug(...)
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Note
|
|
62
|
+
|
|
63
|
+
Declared attributes become available as `@attributeName({ options })` decorators in TypeScript.
|
|
64
|
+
|
|
65
|
+
## Function Signature
|
|
66
|
+
|
|
67
|
+
Rust
|
|
68
|
+
|
|
69
|
+
```
|
|
70
|
+
pub fn my_macro(mut input: TsStream) -> Result<TsStream, MacroforgeError>
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
| Parameter | Description |
|
|
74
|
+
| ----------------------------------- | ------------------------------------------------------- |
|
|
75
|
+
| `input: TsStream` | Token stream containing the class/interface AST |
|
|
76
|
+
| `Result<TsStream, MacroforgeError>` | Returns generated code or an error with source location |
|
|
77
|
+
|
|
78
|
+
## Parsing Input
|
|
79
|
+
|
|
80
|
+
Use `parse_ts_macro_input!` to convert the token stream:
|
|
81
|
+
|
|
82
|
+
Rust
|
|
83
|
+
|
|
84
|
+
```
|
|
85
|
+
use macroforge_ts::ts_syn::{Data, DeriveInput, parse_ts_macro_input};
|
|
46
86
|
|
|
47
87
|
#[ts_macro_derive(MyMacro)]
|
|
48
|
-
pub
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
&
|
|
85
|
-
&
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
struct
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
```
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
88
|
+
pub fn my_macro(mut input: TsStream) -> Result<TsStream, MacroforgeError> {
|
|
89
|
+
let input = parse_ts_macro_input!(input as DeriveInput);
|
|
90
|
+
|
|
91
|
+
// Access class data
|
|
92
|
+
match &input.data {
|
|
93
|
+
Data::Class(class) => {
|
|
94
|
+
let class_name = input.name();
|
|
95
|
+
let fields = class.fields();
|
|
96
|
+
// ...
|
|
97
|
+
}
|
|
98
|
+
Data::Interface(interface) => {
|
|
99
|
+
// Handle interfaces
|
|
100
|
+
}
|
|
101
|
+
Data::Enum(_) => {
|
|
102
|
+
// Handle enums (if supported)
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## DeriveInput Structure
|
|
109
|
+
|
|
110
|
+
Rust
|
|
111
|
+
|
|
112
|
+
```
|
|
113
|
+
struct DeriveInput {
|
|
114
|
+
pub ident: Ident, // The type name
|
|
115
|
+
pub span: SpanIR, // Span of the type definition
|
|
116
|
+
pub attrs: Vec<Attribute>, // Decorators (excluding @derive)
|
|
117
|
+
pub data: Data, // The parsed type data
|
|
118
|
+
pub context: MacroContextIR, // Macro context with spans
|
|
119
|
+
|
|
120
|
+
// Helper methods
|
|
121
|
+
fn name(&self) -> &str; // Get the type name
|
|
122
|
+
fn decorator_span(&self) -> SpanIR; // Span of @derive decorator
|
|
123
|
+
fn as_class(&self) -> Option<&DataClass>;
|
|
124
|
+
fn as_interface(&self) -> Option<&DataInterface>;
|
|
125
|
+
fn as_enum(&self) -> Option<&DataEnum>;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
enum Data {
|
|
129
|
+
Class(DataClass),
|
|
130
|
+
Interface(DataInterface),
|
|
131
|
+
Enum(DataEnum),
|
|
132
|
+
TypeAlias(DataTypeAlias),
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
impl DataClass {
|
|
136
|
+
fn fields(&self) -> &[FieldIR];
|
|
137
|
+
fn methods(&self) -> &[MethodSigIR];
|
|
138
|
+
fn field_names(&self) -> impl Iterator<Item = &str>;
|
|
139
|
+
fn field(&self, name: &str) -> Option<&FieldIR>;
|
|
140
|
+
fn body_span(&self) -> SpanIR; // For inserting code into class body
|
|
141
|
+
fn type_params(&self) -> &[String]; // Generic type parameters
|
|
142
|
+
fn heritage(&self) -> &[String]; // extends/implements clauses
|
|
143
|
+
fn is_abstract(&self) -> bool;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
impl DataInterface {
|
|
147
|
+
fn fields(&self) -> &[InterfaceFieldIR];
|
|
148
|
+
fn methods(&self) -> &[InterfaceMethodIR];
|
|
149
|
+
fn field_names(&self) -> impl Iterator<Item = &str>;
|
|
150
|
+
fn field(&self, name: &str) -> Option<&InterfaceFieldIR>;
|
|
151
|
+
fn body_span(&self) -> SpanIR;
|
|
152
|
+
fn type_params(&self) -> &[String];
|
|
153
|
+
fn heritage(&self) -> &[String]; // extends clauses
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
impl DataEnum {
|
|
157
|
+
fn variants(&self) -> &[EnumVariantIR];
|
|
158
|
+
fn variant_names(&self) -> impl Iterator<Item = &str>;
|
|
159
|
+
fn variant(&self, name: &str) -> Option<&EnumVariantIR>;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
impl DataTypeAlias {
|
|
163
|
+
fn body(&self) -> &TypeBody;
|
|
164
|
+
fn type_params(&self) -> &[String];
|
|
165
|
+
fn is_union(&self) -> bool;
|
|
166
|
+
fn is_object(&self) -> bool;
|
|
167
|
+
fn as_union(&self) -> Option<&[TypeMember]>;
|
|
168
|
+
fn as_object(&self) -> Option<&[InterfaceFieldIR]>;
|
|
169
|
+
}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
## Accessing Field Data
|
|
173
|
+
|
|
174
|
+
### Class Fields (FieldIR)
|
|
175
|
+
|
|
176
|
+
Rust
|
|
177
|
+
|
|
178
|
+
```
|
|
179
|
+
struct FieldIR {
|
|
180
|
+
pub name: String, // Field name
|
|
181
|
+
pub span: SpanIR, // Field span
|
|
182
|
+
pub ts_type: String, // TypeScript type annotation
|
|
183
|
+
pub optional: bool, // Whether field has ?
|
|
184
|
+
pub readonly: bool, // Whether field is readonly
|
|
185
|
+
pub visibility: Visibility, // Public, Protected, Private
|
|
186
|
+
pub decorators: Vec<DecoratorIR>, // Field decorators
|
|
187
|
+
}
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
### Interface Fields (InterfaceFieldIR)
|
|
191
|
+
|
|
192
|
+
Rust
|
|
193
|
+
|
|
194
|
+
```
|
|
195
|
+
struct InterfaceFieldIR {
|
|
196
|
+
pub name: String,
|
|
197
|
+
pub span: SpanIR,
|
|
198
|
+
pub ts_type: String,
|
|
199
|
+
pub optional: bool,
|
|
200
|
+
pub readonly: bool,
|
|
201
|
+
pub decorators: Vec<DecoratorIR>,
|
|
202
|
+
// Note: No visibility field (interfaces are always public)
|
|
203
|
+
}
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
### Enum Variants (EnumVariantIR)
|
|
207
|
+
|
|
208
|
+
Rust
|
|
209
|
+
|
|
210
|
+
```
|
|
211
|
+
struct EnumVariantIR {
|
|
212
|
+
pub name: String,
|
|
213
|
+
pub span: SpanIR,
|
|
214
|
+
pub value: EnumValue, // Auto, String(String), or Number(f64)
|
|
215
|
+
pub decorators: Vec<DecoratorIR>,
|
|
216
|
+
}
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
### Decorator Structure
|
|
220
|
+
|
|
221
|
+
Rust
|
|
222
|
+
|
|
223
|
+
```
|
|
224
|
+
struct DecoratorIR {
|
|
225
|
+
pub name: String, // e.g., "serde"
|
|
226
|
+
pub args_src: String, // Raw args text, e.g., "skip, rename: 'id'"
|
|
227
|
+
pub span: SpanIR,
|
|
228
|
+
}
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
Note
|
|
232
|
+
|
|
233
|
+
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.
|
|
234
|
+
|
|
235
|
+
## Adding Imports
|
|
236
|
+
|
|
237
|
+
If your macro generates code that requires imports, use the `add_import` method on `TsStream`:
|
|
238
|
+
|
|
239
|
+
Rust
|
|
240
|
+
|
|
241
|
+
```
|
|
242
|
+
// Add an import to be inserted at the top of the file
|
|
243
|
+
let mut output = body! {
|
|
244
|
+
validate(): ValidationResult {
|
|
245
|
+
return validateFields(this);
|
|
246
|
+
}
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
// This will add: import { validateFields, ValidationResult } from "my-validation-lib";
|
|
250
|
+
output.add_import("validateFields", "my-validation-lib");
|
|
251
|
+
output.add_import("ValidationResult", "my-validation-lib");
|
|
176
252
|
|
|
177
253
|
Ok(output)
|
|
178
|
-
```
|
|
179
|
-
|
|
180
|
-
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
Note
|
|
257
|
+
|
|
258
|
+
Imports are automatically deduplicated. If the same import already exists in the file, it won't be added again.
|
|
259
|
+
|
|
260
|
+
## Returning Errors
|
|
261
|
+
|
|
262
|
+
Use `MacroforgeError` to report errors with source locations:
|
|
263
|
+
|
|
264
|
+
Rust
|
|
265
|
+
|
|
266
|
+
```
|
|
181
267
|
#[ts_macro_derive(ClassOnly)]
|
|
182
|
-
pub
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
```
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
268
|
+
pub fn class_only(mut input: TsStream) -> Result<TsStream, MacroforgeError> {
|
|
269
|
+
let input = parse_ts_macro_input!(input as DeriveInput);
|
|
270
|
+
|
|
271
|
+
match &input.data {
|
|
272
|
+
Data::Class(_) => {
|
|
273
|
+
// Generate code...
|
|
274
|
+
Ok(body! { /* ... */ })
|
|
275
|
+
}
|
|
276
|
+
_ => Err(MacroforgeError::new(
|
|
277
|
+
input.decorator_span(),
|
|
278
|
+
"@derive(ClassOnly) can only be used on classes",
|
|
279
|
+
)),
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
## Complete Example
|
|
285
|
+
|
|
286
|
+
Rust
|
|
287
|
+
|
|
288
|
+
```
|
|
289
|
+
use macroforge_ts::macros::{ts_macro_derive, body};
|
|
290
|
+
use macroforge_ts::ts_syn::{
|
|
291
|
+
Data, DeriveInput, FieldIR, MacroforgeError, TsStream, parse_ts_macro_input,
|
|
292
|
+
};
|
|
293
|
+
|
|
294
|
+
// Helper function to check if a field has a decorator
|
|
295
|
+
fn has_decorator(field: &FieldIR, name: &str) -> bool {
|
|
296
|
+
field.decorators.iter().any(|d| d.name.eq_ignore_ascii_case(name))
|
|
297
|
+
}
|
|
207
298
|
|
|
208
299
|
#[ts_macro_derive(
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
300
|
+
Validate,
|
|
301
|
+
description = "Generates a validate() method",
|
|
302
|
+
attributes(validate)
|
|
212
303
|
)]
|
|
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
|
-
|
|
240
|
-
|
|
241
|
-
```
|
|
242
|
-
|
|
304
|
+
pub fn derive_validate(mut input: TsStream) -> Result<TsStream, MacroforgeError> {
|
|
305
|
+
let input = parse_ts_macro_input!(input as DeriveInput);
|
|
306
|
+
|
|
307
|
+
match &input.data {
|
|
308
|
+
Data::Class(class) => {
|
|
309
|
+
let validations: Vec<_> = class.fields()
|
|
310
|
+
.iter()
|
|
311
|
+
.filter(|f| has_decorator(f, "validate"))
|
|
312
|
+
.collect();
|
|
313
|
+
|
|
314
|
+
Ok(body! {
|
|
315
|
+
validate(): string[] {
|
|
316
|
+
const errors: string[] = [];
|
|
317
|
+
{#for field in validations}
|
|
318
|
+
if (!this.@{field.name}) {
|
|
319
|
+
errors.push("@{field.name} is required");
|
|
320
|
+
}
|
|
321
|
+
{/for}
|
|
322
|
+
return errors;
|
|
323
|
+
}
|
|
324
|
+
})
|
|
325
|
+
}
|
|
326
|
+
_ => Err(MacroforgeError::new(
|
|
327
|
+
input.decorator_span(),
|
|
328
|
+
"@derive(Validate) only works on classes",
|
|
329
|
+
)),
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
## Next Steps
|
|
335
|
+
|
|
336
|
+
* [Learn the template syntax](../../docs/custom-macros/ts-quote)
|
|
@@ -2,28 +2,40 @@
|
|
|
2
2
|
|
|
3
3
|
For JavaScript template literals (backtick strings), use the `'^...^'` syntax. This outputs actual backticks and passes through `${"${}"}` for JS interpolation:
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Rust
|
|
6
|
+
|
|
7
|
+
```
|
|
6
8
|
let tag_name = "div";
|
|
7
9
|
|
|
8
10
|
let code = ts_template! {
|
|
9
|
-
const html = "'^<@{tag_name}
|
|
11
|
+
const html = "'^<@{tag_name}>${content}</@{tag_name}>^'";
|
|
10
12
|
};
|
|
11
13
|
```
|
|
12
14
|
|
|
13
15
|
**Generates:**
|
|
14
16
|
|
|
15
|
-
|
|
17
|
+
TypeScript
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
const html = `<div>${content}</div>`;
|
|
21
|
+
```
|
|
16
22
|
|
|
17
|
-
You can mix Rust
|
|
23
|
+
You can mix Rust `@{}` interpolation (evaluated at macro expansion time) with JS `${"${}"}` interpolation (evaluated at runtime):
|
|
18
24
|
|
|
19
|
-
|
|
25
|
+
Rust
|
|
26
|
+
|
|
27
|
+
```
|
|
20
28
|
let class_name = "User";
|
|
21
29
|
|
|
22
30
|
let code = ts_template! {
|
|
23
|
-
"'^Hello
|
|
31
|
+
"'^Hello ${this.name}, you are a @{class_name}^'"
|
|
24
32
|
};
|
|
25
33
|
```
|
|
26
34
|
|
|
27
35
|
**Generates:**
|
|
28
36
|
|
|
29
|
-
|
|
37
|
+
TypeScript
|
|
38
|
+
|
|
39
|
+
```
|
|
40
|
+
`Hello ${this.name}, you are a User`
|
|
41
|
+
```
|