@macroforge/mcp-server 0.1.30 → 0.1.32
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/custom-macros/ts-quote/comments-and.md +7 -28
- package/docs/custom-macros/ts-quote/complete-example-json-derive-macro.md +20 -16
- package/docs/custom-macros/ts-quote/interpolation-expr.md +349 -0
- package/docs/custom-macros/ts-quote/local-constants-let.md +34 -0
- package/docs/custom-macros/ts-quote/overview.md +8 -6
- package/docs/custom-macros/ts-quote/quick-reference.md +128 -80
- package/docs/custom-macros/ts-quote/side-effects-do.md +67 -3
- package/docs/custom-macros/ts-quote/tsstream-injection-typescript.md +1 -1
- package/docs/custom-macros/ts-quote/while-loops-while.md +0 -33
- package/docs/custom-macros/ts-quote.md +199 -102
- package/docs/sections.json +14 -74
- package/package.json +4 -2
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
## Comments: `{> ... <}` and `{>> ... <<}`
|
|
1
|
+
## Comments: `{> "..." <}` and `{>> "..." <<}`
|
|
2
2
|
|
|
3
|
-
Since Rust's tokenizer strips
|
|
3
|
+
Since Rust's tokenizer strips whitespace before macros see them, use string literals to preserve exact spacing in comments:
|
|
4
4
|
|
|
5
5
|
### Block Comments
|
|
6
6
|
|
|
7
|
-
Use `{> comment <}` for block comments:
|
|
7
|
+
Use `{> "comment" <}` for block comments:
|
|
8
8
|
|
|
9
9
|
```rust
|
|
10
10
|
let code = ts_template! {
|
|
11
|
-
{> This is a block comment <}
|
|
11
|
+
{> "This is a block comment" <}
|
|
12
12
|
const x = 42;
|
|
13
13
|
};
|
|
14
14
|
```
|
|
@@ -22,12 +22,12 @@ const x = 42;
|
|
|
22
22
|
|
|
23
23
|
### Doc Comments (JSDoc)
|
|
24
24
|
|
|
25
|
-
Use `{>> doc <<}` for JSDoc comments:
|
|
25
|
+
Use `{>> "doc" <<}` for JSDoc comments:
|
|
26
26
|
|
|
27
27
|
```rust
|
|
28
28
|
let code = ts_template! {
|
|
29
|
-
{>> @param {string} name - The user's name <<}
|
|
30
|
-
{>> @returns {string} A greeting message <<}
|
|
29
|
+
{>> "@param {string} name - The user's name" <<}
|
|
30
|
+
{>> "@returns {string} A greeting message" <<}
|
|
31
31
|
function greet(name: string): string {
|
|
32
32
|
return "Hello, " + name;
|
|
33
33
|
}
|
|
@@ -42,25 +42,4 @@ let code = ts_template! {
|
|
|
42
42
|
function greet(name: string): string {
|
|
43
43
|
return "Hello, " + name;
|
|
44
44
|
}
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
### Comments with Interpolation
|
|
48
|
-
|
|
49
|
-
Comments support `@{expr}` interpolation for dynamic content:
|
|
50
|
-
|
|
51
|
-
```rust
|
|
52
|
-
let param_name = "userId";
|
|
53
|
-
let param_type = "number";
|
|
54
|
-
|
|
55
|
-
let code = ts_template! {
|
|
56
|
-
{>> @param {@{param_type}} @{param_name} - The user ID <<}
|
|
57
|
-
function getUser(userId: number) {}
|
|
58
|
-
};
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
**Generates:**
|
|
62
|
-
|
|
63
|
-
```typescript
|
|
64
|
-
/** @param {number} userId - The user ID */
|
|
65
|
-
function getUser(userId: number) {}
|
|
66
45
|
```
|
|
@@ -66,11 +66,11 @@ pub fn derive_json_macro(input: TsStream) -> MacroResult {
|
|
|
66
66
|
|
|
67
67
|
1. **Compile-Time:** The template is parsed during macro expansion
|
|
68
68
|
|
|
69
|
-
2. **String Building:** Generates Rust code that builds a TypeScript string at runtime
|
|
69
|
+
2. **String Building:** Generates Rust code that builds a TypeScript string at runtime
|
|
70
70
|
|
|
71
|
-
3. **SWC Parsing:** The generated string is parsed with SWC to produce a typed AST
|
|
71
|
+
3. **SWC Parsing:** The generated string is parsed with SWC to produce a typed AST
|
|
72
72
|
|
|
73
|
-
4. **Result:** Returns `Stmt` that can be used in `MacroResult` patches
|
|
73
|
+
4. **Result:** Returns `Stmt` that can be used in `MacroResult` patches
|
|
74
74
|
|
|
75
75
|
## Return Type
|
|
76
76
|
|
|
@@ -87,11 +87,11 @@ This shows you exactly what was generated, making debugging easy!
|
|
|
87
87
|
|
|
88
88
|
## Nesting and Regular TypeScript
|
|
89
89
|
|
|
90
|
-
You can mix template syntax with regular TypeScript. Braces
|
|
90
|
+
You can mix template syntax with regular TypeScript. Braces <code >{}</code > are recognized as either:
|
|
91
91
|
|
|
92
92
|
- **Template tags** if they start with `#`, `$`, `:`, or `/`
|
|
93
93
|
|
|
94
|
-
- **Regular TypeScript blocks** otherwise
|
|
94
|
+
- **Regular TypeScript blocks** otherwise
|
|
95
95
|
|
|
96
96
|
```rust
|
|
97
97
|
ts_template! {
|
|
@@ -109,23 +109,27 @@ ts_template! {
|
|
|
109
109
|
## Comparison with Alternatives
|
|
110
110
|
|
|
111
111
|
| `ts_quote!`
|
|
112
|
-
| Compile-time validation, type-safe
|
|
113
|
-
| Can't handle Vec<Stmt>, verbose
|
|
112
|
+
| Compile-time validation, type-safe
|
|
113
|
+
| Can't handle Vec<Stmt>, verbose
|
|
114
|
+
|
|
114
115
|
|
|
115
|
-
|
|
116
|
-
|
|
|
117
|
-
|
|
|
116
|
+
|
|
117
|
+
| `parse_ts_str()`
|
|
118
|
+
| Maximum flexibility
|
|
119
|
+
| Runtime parsing, less readable
|
|
120
|
+
|
|
118
121
|
|
|
119
|
-
|
|
120
|
-
|
|
|
121
|
-
|
|
|
122
|
+
|
|
123
|
+
| `ts_template!`
|
|
124
|
+
| Readable, handles loops/conditions
|
|
125
|
+
| Small runtime parsing overhead
|
|
122
126
|
|
|
123
127
|
## Best Practices
|
|
124
128
|
|
|
125
129
|
1. Use `ts_template!` for complex code generation with loops/conditions
|
|
126
130
|
|
|
127
|
-
2. Use `ts_quote!` for simple, static statements
|
|
131
|
+
2. Use `ts_quote!` for simple, static statements
|
|
128
132
|
|
|
129
|
-
3. Keep templates readable - extract complex logic into variables
|
|
133
|
+
3. Keep templates readable - extract complex logic into variables
|
|
130
134
|
|
|
131
|
-
4. Don't nest templates too deeply - split into helper functions
|
|
135
|
+
4. Don't nest templates too deeply - split into helper functions
|
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
## Interpolation: `@{expr}`
|
|
2
|
+
|
|
3
|
+
Insert Rust expressions into the generated TypeScript:
|
|
4
|
+
|
|
5
|
+
```rust
|
|
6
|
+
let class_name = "User";
|
|
7
|
+
let method = "toString";
|
|
8
|
+
|
|
9
|
+
let code = ts_template! {
|
|
10
|
+
@{class_name}.prototype.@{method} = function() {
|
|
11
|
+
return "User instance";
|
|
12
|
+
};
|
|
13
|
+
};
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
**Generates:**
|
|
17
|
+
|
|
18
|
+
```typescript
|
|
19
|
+
User.prototype.toString = function () {
|
|
20
|
+
return "User instance";
|
|
21
|
+
};
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
<h2 id="ident-blocks">
|
|
25
|
+
Identifier Concatenation: `{| content |}`
|
|
26
|
+
</h2>
|
|
27
|
+
|
|
28
|
+
When you need to build identifiers dynamically (like `getUser`, `setName`), use the ident block syntax. Everything inside `{| |}` is concatenated without spaces:
|
|
29
|
+
|
|
30
|
+
```rust
|
|
31
|
+
let field_name = "User";
|
|
32
|
+
|
|
33
|
+
let code = ts_template! {
|
|
34
|
+
function {|get@{field_name}|}() {
|
|
35
|
+
return this.@{field_name.to_lowercase()};
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
**Generates:**
|
|
41
|
+
|
|
42
|
+
```typescript
|
|
43
|
+
function getUser() {
|
|
44
|
+
return this.user;
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Without ident blocks, `@{}` always adds a space after for readability. Use `{| |}` when you explicitly want concatenation:
|
|
49
|
+
|
|
50
|
+
```rust
|
|
51
|
+
let name = "Status";
|
|
52
|
+
|
|
53
|
+
// With space (default behavior)
|
|
54
|
+
ts_template! { namespace @{name} } // → "namespace Status"
|
|
55
|
+
|
|
56
|
+
// Without space (ident block)
|
|
57
|
+
ts_template! { {|namespace@{name}|} } // → "namespaceStatus"
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Multiple interpolations can be combined:
|
|
61
|
+
|
|
62
|
+
```rust
|
|
63
|
+
let entity = "user";
|
|
64
|
+
let action = "create";
|
|
65
|
+
|
|
66
|
+
ts_template! { {|@{entity}_@{action}|} } // → "user_create"
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
<h2 id="comments">
|
|
70
|
+
Comments: `{> "..." <}` and
|
|
71
|
+
`{>> "..." <<}`
|
|
72
|
+
</h2>
|
|
73
|
+
|
|
74
|
+
Since Rust's tokenizer strips whitespace before macros see them, use string literals to preserve exact spacing in comments:
|
|
75
|
+
|
|
76
|
+
### Block Comments
|
|
77
|
+
|
|
78
|
+
Use `{> "comment" <}` for block comments:
|
|
79
|
+
|
|
80
|
+
```rust
|
|
81
|
+
let code = ts_template! {
|
|
82
|
+
{> "This is a block comment" <}
|
|
83
|
+
const x = 42;
|
|
84
|
+
};
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
**Generates:**
|
|
88
|
+
|
|
89
|
+
```typescript
|
|
90
|
+
/* This is a block comment */
|
|
91
|
+
const x = 42;
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Doc Comments (JSDoc)
|
|
95
|
+
|
|
96
|
+
Use `{>> "doc" <<}` for JSDoc comments:
|
|
97
|
+
|
|
98
|
+
```rust
|
|
99
|
+
let code = ts_template! {
|
|
100
|
+
{>> "@param {string} name - The user's name" <<}
|
|
101
|
+
{>> "@returns {string} A greeting message" <<}
|
|
102
|
+
function greet(name: string): string {
|
|
103
|
+
return "Hello, " + name;
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
**Generates:**
|
|
109
|
+
|
|
110
|
+
```typescript
|
|
111
|
+
/** @param {string} name - The user's name */
|
|
112
|
+
/** @returns {string} A greeting message */
|
|
113
|
+
function greet(name: string): string {
|
|
114
|
+
return "Hello, " + name;
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### Comments with Interpolation
|
|
119
|
+
|
|
120
|
+
Use `format!()` or similar to build dynamic comment strings:
|
|
121
|
+
|
|
122
|
+
```rust
|
|
123
|
+
let param_name = "userId";
|
|
124
|
+
let param_type = "number";
|
|
125
|
+
let comment = format!("@param {{{}}} {} - The user ID", param_type, param_name);
|
|
126
|
+
|
|
127
|
+
let code = ts_template! {
|
|
128
|
+
{>> @{comment} <<}
|
|
129
|
+
function getUser(userId: number) {}
|
|
130
|
+
};
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
**Generates:**
|
|
134
|
+
|
|
135
|
+
```typescript
|
|
136
|
+
/** @param {number} userId - The user ID */
|
|
137
|
+
function getUser(userId: number) {}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
<h2 id="string-interpolation">
|
|
141
|
+
String Interpolation: `"text @{expr}"`
|
|
142
|
+
</h2>
|
|
143
|
+
|
|
144
|
+
Interpolation works automatically inside string literals - no <code >format!()</code > needed:
|
|
145
|
+
|
|
146
|
+
```rust
|
|
147
|
+
let name = "World";
|
|
148
|
+
let count = 42;
|
|
149
|
+
|
|
150
|
+
let code = ts_template! {
|
|
151
|
+
console.log("Hello @{name}!");
|
|
152
|
+
console.log("Count: @{count}, doubled: @{count * 2}");
|
|
153
|
+
};
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
**Generates:**
|
|
157
|
+
|
|
158
|
+
```typescript
|
|
159
|
+
console.log("Hello World!");
|
|
160
|
+
console.log("Count: 42, doubled: 84");
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
This also works with method calls and complex expressions:
|
|
164
|
+
|
|
165
|
+
```rust
|
|
166
|
+
let field = "username";
|
|
167
|
+
|
|
168
|
+
let code = ts_template! {
|
|
169
|
+
throw new Error("Invalid @{field.to_uppercase()}");
|
|
170
|
+
};
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
<h2 id="backtick-templates">
|
|
174
|
+
Backtick Template Literals: `"'^...^'"`
|
|
175
|
+
</h2>
|
|
176
|
+
|
|
177
|
+
For JavaScript template literals (backtick strings), use the <code >'^...^'</code > syntax. This outputs actual backticks and passes through `${"${}"}` for JS interpolation:
|
|
178
|
+
|
|
179
|
+
```rust
|
|
180
|
+
let tag_name = "div";
|
|
181
|
+
|
|
182
|
+
let code = ts_template! {
|
|
183
|
+
const html = "'^<@{tag_name}>\${content}</@{tag_name}>^'";
|
|
184
|
+
};
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
**Generates:**
|
|
188
|
+
|
|
189
|
+
<CodeBlock code={"const html = `${content}`;"} lang="typescript" />
|
|
190
|
+
|
|
191
|
+
You can mix Rust `@{}` interpolation (evaluated at macro expansion time) with JS `${"${}"}` interpolation (evaluated at runtime):
|
|
192
|
+
|
|
193
|
+
```rust
|
|
194
|
+
let class_name = "User";
|
|
195
|
+
|
|
196
|
+
let code = ts_template! {
|
|
197
|
+
"'^Hello \${this.name}, you are a @{class_name}^'"
|
|
198
|
+
};
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
**Generates:**
|
|
202
|
+
|
|
203
|
+
<CodeBlock code={"`Hello ${this.name}, you are a User`"} lang="typescript" />
|
|
204
|
+
|
|
205
|
+
<h2 id="conditionals">
|
|
206
|
+
Conditionals: `{#if}...{/if}`
|
|
207
|
+
</h2>
|
|
208
|
+
|
|
209
|
+
Basic conditional:
|
|
210
|
+
|
|
211
|
+
```rust
|
|
212
|
+
let needs_validation = true;
|
|
213
|
+
|
|
214
|
+
let code = ts_template! {
|
|
215
|
+
function save() {
|
|
216
|
+
{#if needs_validation}
|
|
217
|
+
if (!this.isValid()) return false;
|
|
218
|
+
{/if}
|
|
219
|
+
return this.doSave();
|
|
220
|
+
}
|
|
221
|
+
};
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
### If-Else
|
|
225
|
+
|
|
226
|
+
```rust
|
|
227
|
+
let has_default = true;
|
|
228
|
+
|
|
229
|
+
let code = ts_template! {
|
|
230
|
+
{#if has_default}
|
|
231
|
+
return defaultValue;
|
|
232
|
+
{:else}
|
|
233
|
+
throw new Error("No default");
|
|
234
|
+
{/if}
|
|
235
|
+
};
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
### If-Else-If Chains
|
|
239
|
+
|
|
240
|
+
```rust
|
|
241
|
+
let level = 2;
|
|
242
|
+
|
|
243
|
+
let code = ts_template! {
|
|
244
|
+
{#if level == 1}
|
|
245
|
+
console.log("Level 1");
|
|
246
|
+
{:else if level == 2}
|
|
247
|
+
console.log("Level 2");
|
|
248
|
+
{:else}
|
|
249
|
+
console.log("Other level");
|
|
250
|
+
{/if}
|
|
251
|
+
};
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
<h2 id="pattern-matching">
|
|
255
|
+
Pattern Matching: `{#if let}`
|
|
256
|
+
</h2>
|
|
257
|
+
|
|
258
|
+
Use `if let` for pattern matching on `Option`, `Result`, or other Rust enums:
|
|
259
|
+
|
|
260
|
+
```rust
|
|
261
|
+
let maybe_name: Option<&str> = Some("Alice");
|
|
262
|
+
|
|
263
|
+
let code = ts_template! {
|
|
264
|
+
{#if let Some(name) = maybe_name}
|
|
265
|
+
console.log("Hello, @{name}!");
|
|
266
|
+
{:else}
|
|
267
|
+
console.log("Hello, anonymous!");
|
|
268
|
+
{/if}
|
|
269
|
+
};
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
**Generates:**
|
|
273
|
+
|
|
274
|
+
```typescript
|
|
275
|
+
console.log("Hello, Alice!");
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
This is useful when working with optional values from your IR:
|
|
279
|
+
|
|
280
|
+
```rust
|
|
281
|
+
let code = ts_template! {
|
|
282
|
+
{#if let Some(default_val) = field.default_value}
|
|
283
|
+
this.@{field.name} = @{default_val};
|
|
284
|
+
{:else}
|
|
285
|
+
this.@{field.name} = undefined;
|
|
286
|
+
{/if}
|
|
287
|
+
};
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
<h2 id="match-expressions">
|
|
291
|
+
Match Expressions: `{#match}`
|
|
292
|
+
</h2>
|
|
293
|
+
|
|
294
|
+
Use `match` for exhaustive pattern matching:
|
|
295
|
+
|
|
296
|
+
```rust
|
|
297
|
+
enum Visibility { Public, Private, Protected }
|
|
298
|
+
let visibility = Visibility::Public;
|
|
299
|
+
|
|
300
|
+
let code = ts_template! {
|
|
301
|
+
{#match visibility}
|
|
302
|
+
{:case Visibility::Public}
|
|
303
|
+
public
|
|
304
|
+
{:case Visibility::Private}
|
|
305
|
+
private
|
|
306
|
+
{:case Visibility::Protected}
|
|
307
|
+
protected
|
|
308
|
+
{/match}
|
|
309
|
+
field: string;
|
|
310
|
+
};
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
**Generates:**
|
|
314
|
+
|
|
315
|
+
```typescript
|
|
316
|
+
public field: string;
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
### Match with Value Extraction
|
|
320
|
+
|
|
321
|
+
```rust
|
|
322
|
+
let result: Result<i32, &str> = Ok(42);
|
|
323
|
+
|
|
324
|
+
let code = ts_template! {
|
|
325
|
+
const value = {#match result}
|
|
326
|
+
{:case Ok(val)}
|
|
327
|
+
@{val}
|
|
328
|
+
{:case Err(msg)}
|
|
329
|
+
throw new Error("@{msg}")
|
|
330
|
+
{/match};
|
|
331
|
+
};
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
### Match with Wildcard
|
|
335
|
+
|
|
336
|
+
```rust
|
|
337
|
+
let count = 5;
|
|
338
|
+
|
|
339
|
+
let code = ts_template! {
|
|
340
|
+
{#match count}
|
|
341
|
+
{:case 0}
|
|
342
|
+
console.log("none");
|
|
343
|
+
{:case 1}
|
|
344
|
+
console.log("one");
|
|
345
|
+
{:case _}
|
|
346
|
+
console.log("many");
|
|
347
|
+
{/match}
|
|
348
|
+
};
|
|
349
|
+
```
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
## Local Constants: `{$let}`
|
|
2
|
+
|
|
3
|
+
Define local variables within the template scope:
|
|
4
|
+
|
|
5
|
+
```rust
|
|
6
|
+
let items = vec![("user", "User"), ("post", "Post")];
|
|
7
|
+
|
|
8
|
+
let code = ts_template! {
|
|
9
|
+
{#for (key, class_name) in items}
|
|
10
|
+
{$let upper = class_name.to_uppercase()}
|
|
11
|
+
console.log("Processing @{upper}");
|
|
12
|
+
const @{key} = new @{class_name}();
|
|
13
|
+
{/for}
|
|
14
|
+
};
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
This is useful for computing derived values inside loops without cluttering the Rust code.
|
|
18
|
+
|
|
19
|
+
<h2 id="mutable-variables">
|
|
20
|
+
Mutable Variables: `{$let mut}`
|
|
21
|
+
</h2>
|
|
22
|
+
|
|
23
|
+
When you need to modify a variable within the template (e.g., in a <code >while</code > loop), use `{$let mut}`:
|
|
24
|
+
|
|
25
|
+
```rust
|
|
26
|
+
let code = ts_template! {
|
|
27
|
+
{$let mut count = 0}
|
|
28
|
+
{#for item in items}
|
|
29
|
+
console.log("Item @{count}: @{item}");
|
|
30
|
+
{$do count += 1}
|
|
31
|
+
{/for}
|
|
32
|
+
console.log("Total: @{count}");
|
|
33
|
+
};
|
|
34
|
+
```
|
|
@@ -5,9 +5,11 @@
|
|
|
5
5
|
## Available Macros
|
|
6
6
|
|
|
7
7
|
| `ts_template!`
|
|
8
|
-
| Any TypeScript code
|
|
9
|
-
| General code generation
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
|
8
|
+
| Any TypeScript code
|
|
9
|
+
| General code generation
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
| `body!`
|
|
14
|
+
| Class body members
|
|
15
|
+
| Methods and properties
|