@macroforge/mcp-server 0.1.33 → 0.1.35
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/README.md +68 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +46 -1
- package/dist/index.js.map +1 -1
- package/dist/tools/docs-loader.d.ts +133 -5
- package/dist/tools/docs-loader.d.ts.map +1 -1
- package/dist/tools/docs-loader.js +131 -15
- package/dist/tools/docs-loader.js.map +1 -1
- package/dist/tools/index.d.ts +48 -1
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +163 -14
- package/dist/tools/index.js.map +1 -1
- package/docs/api/api-overview.md +24 -46
- package/docs/api/expand-sync.md +24 -51
- package/docs/api/native-plugin.md +24 -56
- package/docs/api/position-mapper.md +34 -76
- package/docs/api/transform-sync.md +27 -59
- package/docs/builtin-macros/clone.md +45 -104
- package/docs/builtin-macros/debug.md +33 -104
- package/docs/builtin-macros/default.md +78 -114
- package/docs/builtin-macros/deserialize.md +93 -273
- package/docs/builtin-macros/hash.md +58 -100
- package/docs/builtin-macros/macros-overview.md +42 -103
- package/docs/builtin-macros/ord.md +65 -133
- package/docs/builtin-macros/partial-eq.md +53 -179
- package/docs/builtin-macros/partial-ord.md +67 -159
- package/docs/builtin-macros/serialize.md +64 -194
- package/docs/concepts/architecture.md +40 -99
- package/docs/concepts/derive-system.md +129 -125
- package/docs/concepts/how-macros-work.md +52 -84
- package/docs/custom-macros/custom-overview.md +17 -39
- package/docs/custom-macros/rust-setup.md +22 -55
- package/docs/custom-macros/ts-macro-derive.md +43 -107
- package/docs/custom-macros/ts-quote.md +177 -507
- package/docs/getting-started/first-macro.md +108 -33
- package/docs/getting-started/installation.md +32 -73
- package/docs/integration/cli.md +70 -156
- package/docs/integration/configuration.md +32 -75
- package/docs/integration/integration-overview.md +16 -55
- package/docs/integration/mcp-server.md +30 -69
- package/docs/integration/svelte-preprocessor.md +60 -83
- package/docs/integration/typescript-plugin.md +32 -74
- package/docs/integration/vite-plugin.md +30 -79
- package/docs/language-servers/ls-overview.md +22 -46
- package/docs/language-servers/svelte.md +30 -69
- package/docs/language-servers/zed.md +34 -72
- package/docs/roadmap/roadmap.md +54 -130
- package/docs/sections.json +3 -262
- package/package.json +2 -2
|
@@ -1,156 +1,36 @@
|
|
|
1
1
|
# Template Syntax
|
|
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
|
-
| Block comment: outputs `/* comment */` (string preserves
|
|
35
|
-
whitespace)</td
|
|
36
|
-
>
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
<td>`{>> "doc" <<}`
|
|
41
|
-
| Doc comment: outputs `/** doc */` (string preserves whitespace)</td
|
|
42
|
-
>
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
<td>`@@{`
|
|
47
|
-
| Escape for literal `@{` (e.g.,
|
|
48
|
-
`"@@{foo}"`
|
|
49
|
-
→ `@{foo}`)</td
|
|
50
|
-
>
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
<td>`"text @{expr}"`
|
|
55
|
-
| String interpolation (auto-detected)
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
| `"'^template ${js}^'"`
|
|
60
|
-
| JS backtick template literal (outputs <code
|
|
61
|
-
>`template ${js}`</code
|
|
62
|
-
>)</td
|
|
63
|
-
>
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
<td>`{#if cond}...{/if}`
|
|
68
|
-
| Conditional block
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
| <code
|
|
73
|
-
>{#if cond}...{:else}...{/if}</code
|
|
74
|
-
></td
|
|
75
|
-
>
|
|
76
|
-
<td>Conditional with else
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
| <code
|
|
81
|
-
>{#if a}...{:else if
|
|
82
|
-
b}...{:else}...{/if}</code
|
|
83
|
-
></td
|
|
84
|
-
>
|
|
85
|
-
<td>Full if/else-if/else chain
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
| <code
|
|
90
|
-
>{#if let pattern = expr}...{/if}</code
|
|
91
|
-
></td
|
|
92
|
-
>
|
|
93
|
-
<td>Pattern matching if-let
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
| <code
|
|
98
|
-
>{#match expr}{:case
|
|
99
|
-
pattern}...{/match}</code
|
|
100
|
-
></td
|
|
101
|
-
>
|
|
102
|
-
<td>Match expression with case arms
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
| <code>{#for item in list}...{/for}</code
|
|
107
|
-
></td
|
|
108
|
-
>
|
|
109
|
-
<td>Iterate over a collection
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
| `{#while cond}...{/while}`
|
|
114
|
-
| While loop
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
| <code
|
|
119
|
-
>{#while let pattern = expr}...{/while}</code
|
|
120
|
-
></td
|
|
121
|
-
>
|
|
122
|
-
<td>While-let pattern matching loop
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
| `{$let name = expr}`
|
|
127
|
-
| Define a local constant
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
| `{$let mut name = expr}`
|
|
132
|
-
| Define a mutable local variable
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
| `{$do expr}`
|
|
137
|
-
| Execute a side-effectful expression
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
| `{$typescript stream}`
|
|
142
|
-
<td
|
|
143
|
-
>Inject a TsStream, preserving its source and runtime_patches
|
|
144
|
-
(imports)</td
|
|
145
|
-
>
|
|
146
|
-
|
|
147
|
-
**Note:** A single `@` not followed by `{` passes through unchanged (e.g., `email@domain.com` works as expected).
|
|
148
|
-
|
|
149
|
-
## Interpolation: `@{expr}`
|
|
150
|
-
|
|
151
|
-
Insert Rust expressions into the generated TypeScript:
|
|
152
|
-
|
|
153
|
-
```rust
|
|
2
|
+
*The `macroforge_ts_quote` crate provides template-based code generation for TypeScript. The `ts_template!` macro uses Rust-inspired syntax for control flow and interpolation, making it easy to generate complex TypeScript code.*
|
|
3
|
+
## Available Macros
|
|
4
|
+
| Macro | Output | Use Case |
|
|
5
|
+
| --- | --- | --- |
|
|
6
|
+
| `ts_template!` | Any TypeScript code | General code generation |
|
|
7
|
+
| `body!` | Class body members | Methods and properties |
|
|
8
|
+
## Quick Reference
|
|
9
|
+
| Syntax | Description |
|
|
10
|
+
| --- | --- |
|
|
11
|
+
| `@{expr}` | Interpolate a Rust expression (adds space after) |
|
|
12
|
+
| `{| content |}` | Ident block: concatenates without spaces (e.g., `{|get@{name}|}` → `getUser`) |
|
|
13
|
+
| `{> "comment" <}` | Block comment: outputs `/* comment */` (string preserves whitespace) |
|
|
14
|
+
| `{>> "doc" <<}` | Doc comment: outputs `/** doc */` (string preserves whitespace) |
|
|
15
|
+
| `@@{` | Escape for literal `@{` (e.g., `"@@{foo}"` → `@{foo}`) |
|
|
16
|
+
| `"text @{expr}"` | String interpolation (auto-detected) |
|
|
17
|
+
| `"'^template ${js}^'"` | JS backtick template literal (outputs ``template ${js}``) |
|
|
18
|
+
| `{#if cond}...{/if}` | Conditional block |
|
|
19
|
+
| `{#if cond}...{:else}...{/if}` | Conditional with else |
|
|
20
|
+
| {#if a}...{:else if b}...{:else}...{/if} | Full if/else-if/else chain |
|
|
21
|
+
| `{#if let pattern = expr}...{/if}` | Pattern matching if-let |
|
|
22
|
+
| {#match expr}{:case pattern}...{/match} | Match expression with case arms |
|
|
23
|
+
| `{#for item in list}...{/for}` | Iterate over a collection |
|
|
24
|
+
| `{#while cond}...{/while}` | While loop |
|
|
25
|
+
| `{#while let pattern = expr}...{/while}` | While-let pattern matching loop |
|
|
26
|
+
| `{$let name = expr}` | Define a local constant |
|
|
27
|
+
| `{$let mut name = expr}` | Define a mutable local variable |
|
|
28
|
+
| `{$do expr}` | Execute a side-effectful expression |
|
|
29
|
+
| `{$typescript stream}` | Inject a TsStream, preserving its source and runtime_patches (imports) |
|
|
30
|
+
**Note:** A single `@` not followed by `{` passes through unchanged (e.g., `email@domain.com` works as expected).
|
|
31
|
+
## Interpolation: `@{expr}`
|
|
32
|
+
Insert Rust expressions into the generated TypeScript:
|
|
33
|
+
```
|
|
154
34
|
let class_name = "User";
|
|
155
35
|
let method = "toString";
|
|
156
36
|
|
|
@@ -159,23 +39,14 @@ let code = ts_template! {
|
|
|
159
39
|
return "User instance";
|
|
160
40
|
};
|
|
161
41
|
};
|
|
162
|
-
```
|
|
163
|
-
|
|
164
|
-
**Generates:**
|
|
165
|
-
|
|
166
|
-
```typescript
|
|
42
|
+
``` **Generates:**
|
|
43
|
+
```
|
|
167
44
|
User.prototype.toString = function () {
|
|
168
45
|
return "User instance";
|
|
169
46
|
};
|
|
170
|
-
```
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
Identifier Concatenation: `{| content |}`
|
|
174
|
-
</h2>
|
|
175
|
-
|
|
176
|
-
When you need to build identifiers dynamically (like `getUser`, `setName`), use the ident block syntax. Everything inside `{| |}` is concatenated without spaces:
|
|
177
|
-
|
|
178
|
-
```rust
|
|
47
|
+
``` ## Identifier Concatenation: `{| content |}`
|
|
48
|
+
When you need to build identifiers dynamically (like `getUser`, `setName`), use the ident block syntax. Everything inside `{| |}` is concatenated without spaces:
|
|
49
|
+
```
|
|
179
50
|
let field_name = "User";
|
|
180
51
|
|
|
181
52
|
let code = ts_template! {
|
|
@@ -183,19 +54,13 @@ let code = ts_template! {
|
|
|
183
54
|
return this.@{field_name.to_lowercase()};
|
|
184
55
|
}
|
|
185
56
|
};
|
|
186
|
-
```
|
|
187
|
-
|
|
188
|
-
**Generates:**
|
|
189
|
-
|
|
190
|
-
```typescript
|
|
57
|
+
``` **Generates:**
|
|
58
|
+
```
|
|
191
59
|
function getUser() {
|
|
192
60
|
return this.user;
|
|
193
61
|
}
|
|
194
|
-
```
|
|
195
|
-
|
|
196
|
-
Without ident blocks, `@{}` always adds a space after for readability. Use `{| |}` when you explicitly want concatenation:
|
|
197
|
-
|
|
198
|
-
```rust
|
|
62
|
+
``` Without ident blocks, `@{}` always adds a space after for readability. Use `{| |}` when you explicitly want concatenation:
|
|
63
|
+
```
|
|
199
64
|
let name = "Status";
|
|
200
65
|
|
|
201
66
|
// With space (default behavior)
|
|
@@ -203,47 +68,28 @@ ts_template! { namespace @{name} } // → "namespace Status"
|
|
|
203
68
|
|
|
204
69
|
// Without space (ident block)
|
|
205
70
|
ts_template! { {|namespace@{name}|} } // → "namespaceStatus"
|
|
206
|
-
```
|
|
207
|
-
|
|
208
|
-
Multiple interpolations can be combined:
|
|
209
|
-
|
|
210
|
-
```rust
|
|
71
|
+
``` Multiple interpolations can be combined:
|
|
72
|
+
```
|
|
211
73
|
let entity = "user";
|
|
212
74
|
let action = "create";
|
|
213
75
|
|
|
214
76
|
ts_template! { {|@{entity}_@{action}|} } // → "user_create"
|
|
215
|
-
```
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
</h2>
|
|
221
|
-
|
|
222
|
-
Since Rust's tokenizer strips whitespace before macros see them, use string literals to preserve exact spacing in comments:
|
|
223
|
-
|
|
224
|
-
### Block Comments
|
|
225
|
-
|
|
226
|
-
Use `{> "comment" <}` for block comments:
|
|
227
|
-
|
|
228
|
-
```rust
|
|
77
|
+
``` ## Comments: `{> "..." <}` and `{>> "..." <<}`
|
|
78
|
+
Since Rust's tokenizer strips whitespace before macros see them, use string literals to preserve exact spacing in comments:
|
|
79
|
+
### Block Comments
|
|
80
|
+
Use `{> "comment" <}` for block comments:
|
|
81
|
+
```
|
|
229
82
|
let code = ts_template! {
|
|
230
83
|
{> "This is a block comment" <}
|
|
231
84
|
const x = 42;
|
|
232
85
|
};
|
|
233
|
-
```
|
|
234
|
-
|
|
235
|
-
**Generates:**
|
|
236
|
-
|
|
237
|
-
```typescript
|
|
86
|
+
``` **Generates:**
|
|
87
|
+
```
|
|
238
88
|
/* This is a block comment */
|
|
239
89
|
const x = 42;
|
|
240
|
-
```
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
Use `{>> "doc" <<}` for JSDoc comments:
|
|
245
|
-
|
|
246
|
-
```rust
|
|
90
|
+
``` ### Doc Comments (JSDoc)
|
|
91
|
+
Use `{>> "doc" <<}` for JSDoc comments:
|
|
92
|
+
```
|
|
247
93
|
let code = ts_template! {
|
|
248
94
|
{>> "@param {string} name - The user's name" <<}
|
|
249
95
|
{>> "@returns {string} A greeting message" <<}
|
|
@@ -251,23 +97,16 @@ let code = ts_template! {
|
|
|
251
97
|
return "Hello, " + name;
|
|
252
98
|
}
|
|
253
99
|
};
|
|
254
|
-
```
|
|
255
|
-
|
|
256
|
-
**Generates:**
|
|
257
|
-
|
|
258
|
-
```typescript
|
|
100
|
+
``` **Generates:**
|
|
101
|
+
```
|
|
259
102
|
/** @param {string} name - The user's name */
|
|
260
103
|
/** @returns {string} A greeting message */
|
|
261
104
|
function greet(name: string): string {
|
|
262
105
|
return "Hello, " + name;
|
|
263
106
|
}
|
|
264
|
-
```
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
Use `format!()` or similar to build dynamic comment strings:
|
|
269
|
-
|
|
270
|
-
```rust
|
|
107
|
+
``` ### Comments with Interpolation
|
|
108
|
+
Use `format!()` or similar to build dynamic comment strings:
|
|
109
|
+
```
|
|
271
110
|
let param_name = "userId";
|
|
272
111
|
let param_type = "number";
|
|
273
112
|
let comment = format!("@param {{{}}} {} - The user ID", param_type, param_name);
|
|
@@ -276,22 +115,13 @@ let code = ts_template! {
|
|
|
276
115
|
{>> @{comment} <<}
|
|
277
116
|
function getUser(userId: number) {}
|
|
278
117
|
};
|
|
279
|
-
```
|
|
280
|
-
|
|
281
|
-
**Generates:**
|
|
282
|
-
|
|
283
|
-
```typescript
|
|
118
|
+
``` **Generates:**
|
|
119
|
+
```
|
|
284
120
|
/** @param {number} userId - The user ID */
|
|
285
121
|
function getUser(userId: number) {}
|
|
286
|
-
```
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
String Interpolation: `"text @{expr}"`
|
|
290
|
-
</h2>
|
|
291
|
-
|
|
292
|
-
Interpolation works automatically inside string literals - no <code >format!()</code > needed:
|
|
293
|
-
|
|
294
|
-
```rust
|
|
122
|
+
``` ## String Interpolation: `"text @{expr}"`
|
|
123
|
+
Interpolation works automatically inside string literals - no `format!()` needed:
|
|
124
|
+
```
|
|
295
125
|
let name = "World";
|
|
296
126
|
let count = 42;
|
|
297
127
|
|
|
@@ -299,64 +129,41 @@ let code = ts_template! {
|
|
|
299
129
|
console.log("Hello @{name}!");
|
|
300
130
|
console.log("Count: @{count}, doubled: @{count * 2}");
|
|
301
131
|
};
|
|
302
|
-
```
|
|
303
|
-
|
|
304
|
-
**Generates:**
|
|
305
|
-
|
|
306
|
-
```typescript
|
|
132
|
+
``` **Generates:**
|
|
133
|
+
```
|
|
307
134
|
console.log("Hello World!");
|
|
308
135
|
console.log("Count: 42, doubled: 84");
|
|
309
|
-
```
|
|
310
|
-
|
|
311
|
-
This also works with method calls and complex expressions:
|
|
312
|
-
|
|
313
|
-
```rust
|
|
136
|
+
``` This also works with method calls and complex expressions:
|
|
137
|
+
```
|
|
314
138
|
let field = "username";
|
|
315
139
|
|
|
316
140
|
let code = ts_template! {
|
|
317
141
|
throw new Error("Invalid @{field.to_uppercase()}");
|
|
318
142
|
};
|
|
319
|
-
```
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
Backtick Template Literals: `"'^...^'"`
|
|
323
|
-
</h2>
|
|
324
|
-
|
|
325
|
-
For JavaScript template literals (backtick strings), use the <code >'^...^'</code > syntax. This outputs actual backticks and passes through `${"${}"}` for JS interpolation:
|
|
326
|
-
|
|
327
|
-
```rust
|
|
143
|
+
``` ## Backtick Template Literals: `"'^...^'"`
|
|
144
|
+
For JavaScript template literals (backtick strings), use the `'^...^'` syntax. This outputs actual backticks and passes through `$${}` for JS interpolation:
|
|
145
|
+
```
|
|
328
146
|
let tag_name = "div";
|
|
329
147
|
|
|
330
148
|
let code = ts_template! {
|
|
331
|
-
const html = "'^<@{tag_name}
|
|
149
|
+
const html = "'^<@{tag_name}>${content}</@{tag_name}>^'";
|
|
332
150
|
};
|
|
333
|
-
```
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
You can mix Rust `@{}` interpolation (evaluated at macro expansion time) with JS `${"${}"}` interpolation (evaluated at runtime):
|
|
340
|
-
|
|
341
|
-
```rust
|
|
151
|
+
``` **Generates:**
|
|
152
|
+
```
|
|
153
|
+
const html = `${content}`;
|
|
154
|
+
``` You can mix Rust `@{}` interpolation (evaluated at macro expansion time) with JS `$${}` interpolation (evaluated at runtime):
|
|
155
|
+
```
|
|
342
156
|
let class_name = "User";
|
|
343
157
|
|
|
344
158
|
let code = ts_template! {
|
|
345
|
-
"'^Hello
|
|
159
|
+
"'^Hello ${this.name}, you are a @{class_name}^'"
|
|
346
160
|
};
|
|
347
|
-
```
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
<h2 id="conditionals">
|
|
354
|
-
Conditionals: `{#if}...{/if}`
|
|
355
|
-
</h2>
|
|
356
|
-
|
|
357
|
-
Basic conditional:
|
|
358
|
-
|
|
359
|
-
```rust
|
|
161
|
+
``` **Generates:**
|
|
162
|
+
```
|
|
163
|
+
`Hello ${this.name}, you are a User`
|
|
164
|
+
``` ## Conditionals: `{#if}...{/if}`
|
|
165
|
+
Basic conditional:
|
|
166
|
+
```
|
|
360
167
|
let needs_validation = true;
|
|
361
168
|
|
|
362
169
|
let code = ts_template! {
|
|
@@ -367,11 +174,8 @@ let code = ts_template! {
|
|
|
367
174
|
return this.doSave();
|
|
368
175
|
}
|
|
369
176
|
};
|
|
370
|
-
```
|
|
371
|
-
|
|
372
|
-
### If-Else
|
|
373
|
-
|
|
374
|
-
```rust
|
|
177
|
+
``` ### If-Else
|
|
178
|
+
```
|
|
375
179
|
let has_default = true;
|
|
376
180
|
|
|
377
181
|
let code = ts_template! {
|
|
@@ -381,11 +185,8 @@ let code = ts_template! {
|
|
|
381
185
|
throw new Error("No default");
|
|
382
186
|
{/if}
|
|
383
187
|
};
|
|
384
|
-
```
|
|
385
|
-
|
|
386
|
-
### If-Else-If Chains
|
|
387
|
-
|
|
388
|
-
```rust
|
|
188
|
+
``` ### If-Else-If Chains
|
|
189
|
+
```
|
|
389
190
|
let level = 2;
|
|
390
191
|
|
|
391
192
|
let code = ts_template! {
|
|
@@ -397,15 +198,9 @@ let code = ts_template! {
|
|
|
397
198
|
console.log("Other level");
|
|
398
199
|
{/if}
|
|
399
200
|
};
|
|
400
|
-
```
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
Pattern Matching: `{#if let}`
|
|
404
|
-
</h2>
|
|
405
|
-
|
|
406
|
-
Use `if let` for pattern matching on `Option`, `Result`, or other Rust enums:
|
|
407
|
-
|
|
408
|
-
```rust
|
|
201
|
+
``` ## Pattern Matching: `{#if let}`
|
|
202
|
+
Use `if let` for pattern matching on `Option`, `Result`, or other Rust enums:
|
|
203
|
+
```
|
|
409
204
|
let maybe_name: Option<&str> = Some("Alice");
|
|
410
205
|
|
|
411
206
|
let code = ts_template! {
|
|
@@ -415,17 +210,11 @@ let code = ts_template! {
|
|
|
415
210
|
console.log("Hello, anonymous!");
|
|
416
211
|
{/if}
|
|
417
212
|
};
|
|
418
|
-
```
|
|
419
|
-
|
|
420
|
-
**Generates:**
|
|
421
|
-
|
|
422
|
-
```typescript
|
|
213
|
+
``` **Generates:**
|
|
214
|
+
```
|
|
423
215
|
console.log("Hello, Alice!");
|
|
424
|
-
```
|
|
425
|
-
|
|
426
|
-
This is useful when working with optional values from your IR:
|
|
427
|
-
|
|
428
|
-
```rust
|
|
216
|
+
``` This is useful when working with optional values from your IR:
|
|
217
|
+
```
|
|
429
218
|
let code = ts_template! {
|
|
430
219
|
{#if let Some(default_val) = field.default_value}
|
|
431
220
|
this.@{field.name} = @{default_val};
|
|
@@ -433,15 +222,9 @@ let code = ts_template! {
|
|
|
433
222
|
this.@{field.name} = undefined;
|
|
434
223
|
{/if}
|
|
435
224
|
};
|
|
436
|
-
```
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
Match Expressions: `{#match}`
|
|
440
|
-
</h2>
|
|
441
|
-
|
|
442
|
-
Use `match` for exhaustive pattern matching:
|
|
443
|
-
|
|
444
|
-
```rust
|
|
225
|
+
``` ## Match Expressions: `{#match}`
|
|
226
|
+
Use `match` for exhaustive pattern matching:
|
|
227
|
+
```
|
|
445
228
|
enum Visibility { Public, Private, Protected }
|
|
446
229
|
let visibility = Visibility::Public;
|
|
447
230
|
|
|
@@ -456,17 +239,11 @@ let code = ts_template! {
|
|
|
456
239
|
{/match}
|
|
457
240
|
field: string;
|
|
458
241
|
};
|
|
459
|
-
```
|
|
460
|
-
|
|
461
|
-
**Generates:**
|
|
462
|
-
|
|
463
|
-
```typescript
|
|
242
|
+
``` **Generates:**
|
|
243
|
+
```
|
|
464
244
|
public field: string;
|
|
465
|
-
```
|
|
466
|
-
|
|
467
|
-
### Match with Value Extraction
|
|
468
|
-
|
|
469
|
-
```rust
|
|
245
|
+
``` ### Match with Value Extraction
|
|
246
|
+
```
|
|
470
247
|
let result: Result<i32, &str> = Ok(42);
|
|
471
248
|
|
|
472
249
|
let code = ts_template! {
|
|
@@ -477,11 +254,8 @@ let code = ts_template! {
|
|
|
477
254
|
throw new Error("@{msg}")
|
|
478
255
|
{/match};
|
|
479
256
|
};
|
|
480
|
-
```
|
|
481
|
-
|
|
482
|
-
### Match with Wildcard
|
|
483
|
-
|
|
484
|
-
```rust
|
|
257
|
+
``` ### Match with Wildcard
|
|
258
|
+
```
|
|
485
259
|
let count = 5;
|
|
486
260
|
|
|
487
261
|
let code = ts_template! {
|
|
@@ -494,11 +268,8 @@ let code = ts_template! {
|
|
|
494
268
|
console.log("many");
|
|
495
269
|
{/match}
|
|
496
270
|
};
|
|
497
|
-
```
|
|
498
|
-
|
|
499
|
-
## Iteration: `{#for}`
|
|
500
|
-
|
|
501
|
-
```rust
|
|
271
|
+
``` ## Iteration: `{#for}`
|
|
272
|
+
```
|
|
502
273
|
let fields = vec!["name", "email", "age"];
|
|
503
274
|
|
|
504
275
|
let code = ts_template! {
|
|
@@ -510,11 +281,8 @@ let code = ts_template! {
|
|
|
510
281
|
return result;
|
|
511
282
|
}
|
|
512
283
|
};
|
|
513
|
-
```
|
|
514
|
-
|
|
515
|
-
**Generates:**
|
|
516
|
-
|
|
517
|
-
```typescript
|
|
284
|
+
``` **Generates:**
|
|
285
|
+
```
|
|
518
286
|
function toJSON() {
|
|
519
287
|
const result = {};
|
|
520
288
|
result.name = this.name;
|
|
@@ -522,11 +290,8 @@ function toJSON() {
|
|
|
522
290
|
result.age = this.age;
|
|
523
291
|
return result;
|
|
524
292
|
}
|
|
525
|
-
```
|
|
526
|
-
|
|
527
|
-
### Tuple Destructuring in Loops
|
|
528
|
-
|
|
529
|
-
```rust
|
|
293
|
+
``` ### Tuple Destructuring in Loops
|
|
294
|
+
```
|
|
530
295
|
let items = vec![("user", "User"), ("post", "Post")];
|
|
531
296
|
|
|
532
297
|
let code = ts_template! {
|
|
@@ -534,11 +299,8 @@ let code = ts_template! {
|
|
|
534
299
|
const @{key} = new @{class_name}();
|
|
535
300
|
{/for}
|
|
536
301
|
};
|
|
537
|
-
```
|
|
538
|
-
|
|
539
|
-
### Nested Iterations
|
|
540
|
-
|
|
541
|
-
```rust
|
|
302
|
+
``` ### Nested Iterations
|
|
303
|
+
```
|
|
542
304
|
let classes = vec![
|
|
543
305
|
("User", vec!["name", "email"]),
|
|
544
306
|
("Post", vec!["title", "content"]),
|
|
@@ -555,13 +317,9 @@ ts_template! {
|
|
|
555
317
|
};
|
|
556
318
|
{/for}
|
|
557
319
|
}
|
|
558
|
-
```
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
Use `while` for loops that need to continue until a condition is false:
|
|
563
|
-
|
|
564
|
-
```rust
|
|
320
|
+
``` ## While Loops: `{#while}`
|
|
321
|
+
Use `while` for loops that need to continue until a condition is false:
|
|
322
|
+
```
|
|
565
323
|
let items = get_items();
|
|
566
324
|
let mut idx = 0;
|
|
567
325
|
|
|
@@ -572,13 +330,9 @@ let code = ts_template! {
|
|
|
572
330
|
{$do i += 1}
|
|
573
331
|
{/while}
|
|
574
332
|
};
|
|
575
|
-
```
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
Use `while let` for iterating with pattern matching, similar to `if let`:
|
|
580
|
-
|
|
581
|
-
```rust
|
|
333
|
+
``` ### While-Let Pattern Matching
|
|
334
|
+
Use `while let` for iterating with pattern matching, similar to `if let`:
|
|
335
|
+
```
|
|
582
336
|
let mut items = vec!["a", "b", "c"].into_iter();
|
|
583
337
|
|
|
584
338
|
let code = ts_template! {
|
|
@@ -586,31 +340,21 @@ let code = ts_template! {
|
|
|
586
340
|
console.log("@{item}");
|
|
587
341
|
{/while}
|
|
588
342
|
};
|
|
589
|
-
```
|
|
590
|
-
|
|
591
|
-
**Generates:**
|
|
592
|
-
|
|
593
|
-
```typescript
|
|
343
|
+
``` **Generates:**
|
|
344
|
+
```
|
|
594
345
|
console.log("a");
|
|
595
346
|
console.log("b");
|
|
596
347
|
console.log("c");
|
|
597
|
-
```
|
|
598
|
-
|
|
599
|
-
This is especially useful when working with iterators or consuming optional values:
|
|
600
|
-
|
|
601
|
-
```rust
|
|
348
|
+
``` This is especially useful when working with iterators or consuming optional values:
|
|
349
|
+
```
|
|
602
350
|
let code = ts_template! {
|
|
603
351
|
{#while let Some(next_field) = remaining_fields.pop()}
|
|
604
352
|
result.@{next_field.name} = this.@{next_field.name};
|
|
605
353
|
{/while}
|
|
606
354
|
};
|
|
607
|
-
```
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
Define local variables within the template scope:
|
|
612
|
-
|
|
613
|
-
```rust
|
|
355
|
+
``` ## Local Constants: `{$let}`
|
|
356
|
+
Define local variables within the template scope:
|
|
357
|
+
```
|
|
614
358
|
let items = vec![("user", "User"), ("post", "Post")];
|
|
615
359
|
|
|
616
360
|
let code = ts_template! {
|
|
@@ -620,17 +364,10 @@ let code = ts_template! {
|
|
|
620
364
|
const @{key} = new @{class_name}();
|
|
621
365
|
{/for}
|
|
622
366
|
};
|
|
623
|
-
```
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
<h2 id="mutable-variables">
|
|
628
|
-
Mutable Variables: `{$let mut}`
|
|
629
|
-
</h2>
|
|
630
|
-
|
|
631
|
-
When you need to modify a variable within the template (e.g., in a <code >while</code > loop), use `{$let mut}`:
|
|
632
|
-
|
|
633
|
-
```rust
|
|
367
|
+
``` This is useful for computing derived values inside loops without cluttering the Rust code.
|
|
368
|
+
## Mutable Variables: `{$let mut}`
|
|
369
|
+
When you need to modify a variable within the template (e.g., in a `while` loop), use `{$let mut}`:
|
|
370
|
+
```
|
|
634
371
|
let code = ts_template! {
|
|
635
372
|
{$let mut count = 0}
|
|
636
373
|
{#for item in items}
|
|
@@ -639,13 +376,9 @@ let code = ts_template! {
|
|
|
639
376
|
{/for}
|
|
640
377
|
console.log("Total: @{count}");
|
|
641
378
|
};
|
|
642
|
-
```
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
Execute an expression for its side effects without producing output. This is commonly used with mutable variables:
|
|
647
|
-
|
|
648
|
-
```rust
|
|
379
|
+
``` ## Side Effects: `{$do}`
|
|
380
|
+
Execute an expression for its side effects without producing output. This is commonly used with mutable variables:
|
|
381
|
+
```
|
|
649
382
|
let code = ts_template! {
|
|
650
383
|
{$let mut results: Vec<String> = Vec::new()}
|
|
651
384
|
{#for field in fields}
|
|
@@ -653,25 +386,14 @@ let code = ts_template! {
|
|
|
653
386
|
{/for}
|
|
654
387
|
return [@{results.join(", ")}];
|
|
655
388
|
};
|
|
656
|
-
```
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
- Setting flags: `{$do found = true}`
|
|
665
|
-
|
|
666
|
-
- Any mutating operation
|
|
667
|
-
|
|
668
|
-
<h2 id="typescript-injection">
|
|
669
|
-
TsStream Injection: `{$typescript}`
|
|
670
|
-
</h2>
|
|
671
|
-
|
|
672
|
-
Inject another TsStream into your template, preserving both its source code and runtime patches (like imports added via `add_import()`):
|
|
673
|
-
|
|
674
|
-
```rust
|
|
389
|
+
``` Common uses for `{$do}`:
|
|
390
|
+
- Incrementing counters: `{$do i += 1}`
|
|
391
|
+
- Building collections: `{$do vec.push(item)}`
|
|
392
|
+
- Setting flags: `{$do found = true}`
|
|
393
|
+
- Any mutating operation
|
|
394
|
+
## TsStream Injection: `{$typescript}`
|
|
395
|
+
Inject another TsStream into your template, preserving both its source code and runtime patches (like imports added via `add_import()`):
|
|
396
|
+
```
|
|
675
397
|
// Create a helper method with its own import
|
|
676
398
|
let mut helper = body! {
|
|
677
399
|
validateEmail(email: string): boolean {
|
|
@@ -689,11 +411,8 @@ let result = body! {
|
|
|
689
411
|
}
|
|
690
412
|
};
|
|
691
413
|
// result now includes helper's source AND its Result import
|
|
692
|
-
```
|
|
693
|
-
|
|
694
|
-
This is essential for composing multiple macro outputs while preserving imports and patches:
|
|
695
|
-
|
|
696
|
-
```rust
|
|
414
|
+
``` This is essential for composing multiple macro outputs while preserving imports and patches:
|
|
415
|
+
```
|
|
697
416
|
let extra_methods = if include_validation {
|
|
698
417
|
Some(body! {
|
|
699
418
|
validate(): boolean { return true; }
|
|
@@ -709,33 +428,21 @@ body! {
|
|
|
709
428
|
{$typescript methods}
|
|
710
429
|
{/if}
|
|
711
430
|
}
|
|
712
|
-
```
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
If you need a literal `@{` in your output (not interpolation), use `@@{`:
|
|
717
|
-
|
|
718
|
-
```rust
|
|
431
|
+
``` ## Escape Syntax
|
|
432
|
+
If you need a literal `@{` in your output (not interpolation), use `@@{`:
|
|
433
|
+
```
|
|
719
434
|
ts_template! {
|
|
720
435
|
// This outputs a literal @{foo}
|
|
721
436
|
const example = "Use @@{foo} for templates";
|
|
722
437
|
}
|
|
723
|
-
```
|
|
724
|
-
|
|
725
|
-
**Generates:**
|
|
726
|
-
|
|
727
|
-
```typescript
|
|
438
|
+
``` **Generates:**
|
|
439
|
+
```
|
|
728
440
|
// This outputs a literal @{foo}
|
|
729
441
|
const example = "Use @{foo} for templates";
|
|
730
|
-
```
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
Here's a comparison showing how `ts_template!` simplifies code generation:
|
|
735
|
-
|
|
736
|
-
### Before (Manual AST Building)
|
|
737
|
-
|
|
738
|
-
```rust
|
|
442
|
+
``` ## Complete Example: JSON Derive Macro
|
|
443
|
+
Here's a comparison showing how `ts_template!` simplifies code generation:
|
|
444
|
+
### Before (Manual AST Building)
|
|
445
|
+
```
|
|
739
446
|
pub fn derive_json_macro(input: TsStream) -> MacroResult {
|
|
740
447
|
let input = parse_ts_macro_input!(input as DeriveInput);
|
|
741
448
|
|
|
@@ -764,11 +471,8 @@ pub fn derive_json_macro(input: TsStream) -> MacroResult {
|
|
|
764
471
|
}
|
|
765
472
|
}
|
|
766
473
|
}
|
|
767
|
-
```
|
|
768
|
-
|
|
769
|
-
### After (With ts_template!)
|
|
770
|
-
|
|
771
|
-
```rust
|
|
474
|
+
``` ### After (With ts_template!)
|
|
475
|
+
```
|
|
772
476
|
pub fn derive_json_macro(input: TsStream) -> MacroResult {
|
|
773
477
|
let input = parse_ts_macro_input!(input as DeriveInput);
|
|
774
478
|
|
|
@@ -791,40 +495,24 @@ pub fn derive_json_macro(input: TsStream) -> MacroResult {
|
|
|
791
495
|
}
|
|
792
496
|
}
|
|
793
497
|
}
|
|
794
|
-
```
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
3. **SWC Parsing:** The generated string is parsed with SWC to produce a typed AST
|
|
803
|
-
|
|
804
|
-
4. **Result:** Returns `Stmt` that can be used in `MacroResult` patches
|
|
805
|
-
|
|
806
|
-
## Return Type
|
|
807
|
-
|
|
808
|
-
`ts_template!` returns a `Result<Stmt, TsSynError>` by default. The macro automatically unwraps and provides helpful error messages showing the generated TypeScript code if parsing fails:
|
|
809
|
-
|
|
810
|
-
```text
|
|
498
|
+
``` ## How It Works
|
|
499
|
+
1. **Compile-Time:** The template is parsed during macro expansion
|
|
500
|
+
2. **String Building:** Generates Rust code that builds a TypeScript string at runtime
|
|
501
|
+
3. **SWC Parsing:** The generated string is parsed with SWC to produce a typed AST
|
|
502
|
+
4. **Result:** Returns `Stmt` that can be used in `MacroResult` patches
|
|
503
|
+
## Return Type
|
|
504
|
+
`ts_template!` returns a `Result<Stmt, TsSynError>` by default. The macro automatically unwraps and provides helpful error messages showing the generated TypeScript code if parsing fails:
|
|
505
|
+
```
|
|
811
506
|
Failed to parse generated TypeScript:
|
|
812
507
|
User.prototype.toJSON = function( {
|
|
813
508
|
return {};
|
|
814
509
|
}
|
|
815
|
-
```
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
You can mix template syntax with regular TypeScript. Braces <code >{}</code > are recognized as either:
|
|
822
|
-
|
|
823
|
-
- **Template tags** if they start with `#`, `$`, `:`, or `/`
|
|
824
|
-
|
|
825
|
-
- **Regular TypeScript blocks** otherwise
|
|
826
|
-
|
|
827
|
-
```rust
|
|
510
|
+
``` This shows you exactly what was generated, making debugging easy!
|
|
511
|
+
## Nesting and Regular TypeScript
|
|
512
|
+
You can mix template syntax with regular TypeScript. Braces `{}` are recognized as either:
|
|
513
|
+
- **Template tags** if they start with `#`, `$`, `:`, or `/`
|
|
514
|
+
- **Regular TypeScript blocks** otherwise
|
|
515
|
+
```
|
|
828
516
|
ts_template! {
|
|
829
517
|
const config = {
|
|
830
518
|
{#if use_strict}
|
|
@@ -835,32 +523,14 @@ ts_template! {
|
|
|
835
523
|
timeout: 5000
|
|
836
524
|
};
|
|
837
525
|
}
|
|
838
|
-
```
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
| `
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
| Maximum flexibility
|
|
850
|
-
| Runtime parsing, less readable
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
| `ts_template!`
|
|
855
|
-
| Readable, handles loops/conditions
|
|
856
|
-
| Small runtime parsing overhead
|
|
857
|
-
|
|
858
|
-
## Best Practices
|
|
859
|
-
|
|
860
|
-
1. Use `ts_template!` for complex code generation with loops/conditions
|
|
861
|
-
|
|
862
|
-
2. Use `ts_quote!` for simple, static statements
|
|
863
|
-
|
|
864
|
-
3. Keep templates readable - extract complex logic into variables
|
|
865
|
-
|
|
866
|
-
4. Don't nest templates too deeply - split into helper functions
|
|
526
|
+
``` ## Comparison with Alternatives
|
|
527
|
+
| Approach | Pros | Cons |
|
|
528
|
+
| --- | --- | --- |
|
|
529
|
+
| `ts_quote!` | Compile-time validation, type-safe | Can't handle Vec<Stmt>, verbose |
|
|
530
|
+
| `parse_ts_str()` | Maximum flexibility | Runtime parsing, less readable |
|
|
531
|
+
| `ts_template!` | Readable, handles loops/conditions | Small runtime parsing overhead |
|
|
532
|
+
## Best Practices
|
|
533
|
+
1. Use `ts_template!` for complex code generation with loops/conditions
|
|
534
|
+
2. Use `ts_quote!` for simple, static statements
|
|
535
|
+
3. Keep templates readable - extract complex logic into variables
|
|
536
|
+
4. Don't nest templates too deeply - split into helper functions
|