@macroforge/mcp-server 0.1.37 → 0.1.39

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.
Files changed (43) hide show
  1. package/docs/api/api-overview.md +13 -13
  2. package/docs/api/expand-sync.md +8 -8
  3. package/docs/api/native-plugin.md +15 -15
  4. package/docs/api/position-mapper.md +6 -6
  5. package/docs/api/transform-sync.md +11 -11
  6. package/docs/builtin-macros/clone.md +43 -23
  7. package/docs/builtin-macros/debug.md +50 -18
  8. package/docs/builtin-macros/default.md +79 -28
  9. package/docs/builtin-macros/deserialize/cycleforward-reference-support.md +11 -0
  10. package/docs/builtin-macros/deserialize/example.md +1625 -0
  11. package/docs/builtin-macros/deserialize/overview.md +15 -10
  12. package/docs/builtin-macros/deserialize/union-type-deserialization.md +27 -0
  13. package/docs/builtin-macros/deserialize/validation.md +34 -0
  14. package/docs/builtin-macros/deserialize.md +1608 -23
  15. package/docs/builtin-macros/hash.md +87 -20
  16. package/docs/builtin-macros/macros-overview.md +40 -40
  17. package/docs/builtin-macros/ord.md +56 -31
  18. package/docs/builtin-macros/partial-eq/example.md +526 -0
  19. package/docs/builtin-macros/partial-eq/overview.md +39 -0
  20. package/docs/builtin-macros/partial-eq.md +184 -26
  21. package/docs/builtin-macros/partial-ord.md +68 -30
  22. package/docs/builtin-macros/serialize/example.md +139 -0
  23. package/docs/builtin-macros/serialize/overview.md +32 -0
  24. package/docs/builtin-macros/serialize/type-specific-serialization.md +22 -0
  25. package/docs/builtin-macros/serialize.md +130 -28
  26. package/docs/concepts/architecture.md +2 -2
  27. package/docs/concepts/derive-system.md +25 -39
  28. package/docs/concepts/how-macros-work.md +8 -4
  29. package/docs/custom-macros/custom-overview.md +23 -23
  30. package/docs/custom-macros/rust-setup.md +31 -31
  31. package/docs/custom-macros/ts-macro-derive.md +107 -107
  32. package/docs/custom-macros/ts-quote.md +226 -226
  33. package/docs/getting-started/first-macro.md +38 -28
  34. package/docs/getting-started/installation.md +15 -15
  35. package/docs/integration/cli.md +9 -9
  36. package/docs/integration/configuration.md +16 -16
  37. package/docs/integration/mcp-server.md +6 -6
  38. package/docs/integration/svelte-preprocessor.md +40 -41
  39. package/docs/integration/typescript-plugin.md +13 -12
  40. package/docs/integration/vite-plugin.md +12 -12
  41. package/docs/language-servers/zed.md +1 -1
  42. package/docs/sections.json +88 -2
  43. package/package.json +2 -2
@@ -34,55 +34,55 @@
34
34
  let class_name = "User";
35
35
  let method = "toString";
36
36
 
37
- let code = ts_template! {
38
- @{class_name}.prototype.@{method} = function() {
37
+ let code = ts_template! {
38
+ @{class_name}.prototype.@{method} = function() {
39
39
  return "User instance";
40
- };
41
- };
40
+ };
41
+ };
42
42
  ``` **Generates:**
43
43
  ```
44
- User.prototype.toString = function () {
44
+ User.prototype.toString = function () {
45
45
  return "User instance";
46
- };
46
+ };
47
47
  ``` ## Identifier Concatenation: `{| content |}`
48
48
  When you need to build identifiers dynamically (like `getUser`, `setName`), use the ident block syntax. Everything inside `{| |}` is concatenated without spaces:
49
49
  ```
50
50
  let field_name = "User";
51
51
 
52
- let code = ts_template! {
53
- function {|get@{field_name}|}() {
54
- return this.@{field_name.to_lowercase()};
55
- }
56
- };
52
+ let code = ts_template! {
53
+ function {|get@{field_name}|}() {
54
+ return this.@{field_name.to_lowercase()};
55
+ }
56
+ };
57
57
  ``` **Generates:**
58
58
  ```
59
- function getUser() {
59
+ function getUser() {
60
60
  return this.user;
61
- }
61
+ }
62
62
  ``` Without ident blocks, `@{}` always adds a space after for readability. Use `{| |}` when you explicitly want concatenation:
63
63
  ```
64
64
  let name = "Status";
65
65
 
66
66
  // With space (default behavior)
67
- ts_template! { namespace @{name} } // → "namespace Status"
67
+ ts_template! { namespace @{name} } // → "namespace Status"
68
68
 
69
69
  // Without space (ident block)
70
- ts_template! { {|namespace@{name}|} } // → "namespaceStatus"
70
+ ts_template! { {|namespace@{name}|} } // → "namespaceStatus"
71
71
  ``` Multiple interpolations can be combined:
72
72
  ```
73
73
  let entity = "user";
74
74
  let action = "create";
75
75
 
76
- ts_template! { {|@{entity}_@{action}|} } // → "user_create"
76
+ ts_template! { {|@{entity}_@{action}|} } // → "user_create"
77
77
  ``` ## Comments: `{> "..." <}` and `{>> "..." <<}`
78
78
  Since Rust's tokenizer strips whitespace before macros see them, use string literals to preserve exact spacing in comments:
79
79
  ### Block Comments
80
80
  Use `{> "comment" <}` for block comments:
81
81
  ```
82
- let code = ts_template! {
83
- {> "This is a block comment" <}
82
+ let code = ts_template! &#123;
83
+ &#123;> "This is a block comment" &#x3C;&#125;
84
84
  const x = 42;
85
- };
85
+ &#125;;
86
86
  ``` **Generates:**
87
87
  ```
88
88
  /* This is a block comment */
@@ -90,45 +90,45 @@ const x = 42;
90
90
  ``` ### Doc Comments (JSDoc)
91
91
  Use `{>> "doc" <<}` for JSDoc comments:
92
92
  ```
93
- let code = ts_template! {
94
- {>> "@param {string} name - The user's name" <<}
95
- {>> "@returns {string} A greeting message" <<}
96
- function greet(name: string): string {
93
+ let code = ts_template! &#123;
94
+ &#123;>> "@param &#123;string&#125; name - The user's name" &#x3C;&#x3C;&#125;
95
+ &#123;>> "@returns &#123;string&#125; A greeting message" &#x3C;&#x3C;&#125;
96
+ function greet(name: string): string &#123;
97
97
  return "Hello, " + name;
98
- }
99
- };
98
+ &#125;
99
+ &#125;;
100
100
  ``` **Generates:**
101
101
  ```
102
- /** @param {string} name - The user's name */
103
- /** @returns {string} A greeting message */
104
- function greet(name: string): string {
102
+ /** @param &#123;string&#125; name - The user's name */
103
+ /** @returns &#123;string&#125; A greeting message */
104
+ function greet(name: string): string &#123;
105
105
  return "Hello, " + name;
106
- }
106
+ &#125;
107
107
  ``` ### Comments with Interpolation
108
108
  Use `format!()` or similar to build dynamic comment strings:
109
109
  ```
110
110
  let param_name = "userId";
111
111
  let param_type = "number";
112
- let comment = format!("@param {{{}}} {} - The user ID", param_type, param_name);
112
+ let comment = format!("@param &#123;&#123;&#123;&#125;&#125;&#125; &#123;&#125; - The user ID", param_type, param_name);
113
113
 
114
- let code = ts_template! {
115
- {>> @{comment} <<}
116
- function getUser(userId: number) {}
117
- };
114
+ let code = ts_template! &#123;
115
+ &#123;>> @&#123;comment&#125; &#x3C;&#x3C;&#125;
116
+ function getUser(userId: number) &#123;&#125;
117
+ &#125;;
118
118
  ``` **Generates:**
119
119
  ```
120
- /** @param {number} userId - The user ID */
121
- function getUser(userId: number) {}
120
+ /** @param &#123;number&#125; userId - The user ID */
121
+ function getUser(userId: number) &#123;&#125;
122
122
  ``` ## String Interpolation: `"text @{expr}"`
123
123
  Interpolation works automatically inside string literals - no `format!()` needed:
124
124
  ```
125
125
  let name = "World";
126
126
  let count = 42;
127
127
 
128
- let code = ts_template! {
129
- console.log("Hello @{name}!");
130
- console.log("Count: @{count}, doubled: @{count * 2}");
131
- };
128
+ let code = ts_template! &#123;
129
+ console.log("Hello @&#123;name&#125;!");
130
+ console.log("Count: @&#123;count&#125;, doubled: @&#123;count * 2&#125;");
131
+ &#125;;
132
132
  ``` **Generates:**
133
133
  ```
134
134
  console.log("Hello World!");
@@ -137,9 +137,9 @@ console.log("Count: 42, doubled: 84");
137
137
  ```
138
138
  let field = "username";
139
139
 
140
- let code = ts_template! {
141
- throw new Error("Invalid @{field.to_uppercase()}");
142
- };
140
+ let code = ts_template! &#123;
141
+ throw new Error("Invalid @&#123;field.to_uppercase()&#125;");
142
+ &#125;;
143
143
  ``` ## Backtick Template Literals: `"'^...^'"`
144
144
  For JavaScript template literals (backtick strings), use the `'^...^'` syntax. This outputs actual backticks and passes through `$${}` for JS interpolation:
145
145
  ```
@@ -160,145 +160,145 @@ let code = ts_template! {
160
160
  };
161
161
  ``` **Generates:**
162
162
  ```
163
- `Hello ${this.name}, you are a User`
163
+ `Hello $&#123;this.name&#125;, you are a User`
164
164
  ``` ## Conditionals: `{#if}...{/if}`
165
165
  Basic conditional:
166
166
  ```
167
167
  let needs_validation = true;
168
168
 
169
- let code = ts_template! {
170
- function save() {
171
- {#if needs_validation}
169
+ let code = ts_template! &#123;
170
+ function save() &#123;
171
+ &#123;#if needs_validation&#125;
172
172
  if (!this.isValid()) return false;
173
- {/if}
173
+ &#123;/if&#125;
174
174
  return this.doSave();
175
- }
176
- };
175
+ &#125;
176
+ &#125;;
177
177
  ``` ### If-Else
178
178
  ```
179
179
  let has_default = true;
180
180
 
181
- let code = ts_template! {
182
- {#if has_default}
181
+ let code = ts_template! &#123;
182
+ &#123;#if has_default&#125;
183
183
  return defaultValue;
184
- {:else}
184
+ &#123;:else&#125;
185
185
  throw new Error("No default");
186
- {/if}
187
- };
186
+ &#123;/if&#125;
187
+ &#125;;
188
188
  ``` ### If-Else-If Chains
189
189
  ```
190
190
  let level = 2;
191
191
 
192
- let code = ts_template! {
193
- {#if level == 1}
192
+ let code = ts_template! &#123;
193
+ &#123;#if level == 1&#125;
194
194
  console.log("Level 1");
195
- {:else if level == 2}
195
+ &#123;:else if level == 2&#125;
196
196
  console.log("Level 2");
197
- {:else}
197
+ &#123;:else&#125;
198
198
  console.log("Other level");
199
- {/if}
200
- };
199
+ &#123;/if&#125;
200
+ &#125;;
201
201
  ``` ## Pattern Matching: `{#if let}`
202
202
  Use `if let` for pattern matching on `Option`, `Result`, or other Rust enums:
203
203
  ```
204
- let maybe_name: Option<&str> = Some("Alice");
204
+ let maybe_name: Option&#x3C;&#x26;str> = Some("Alice");
205
205
 
206
- let code = ts_template! {
207
- {#if let Some(name) = maybe_name}
208
- console.log("Hello, @{name}!");
209
- {:else}
206
+ let code = ts_template! &#123;
207
+ &#123;#if let Some(name) = maybe_name&#125;
208
+ console.log("Hello, @&#123;name&#125;!");
209
+ &#123;:else&#125;
210
210
  console.log("Hello, anonymous!");
211
- {/if}
212
- };
211
+ &#123;/if&#125;
212
+ &#125;;
213
213
  ``` **Generates:**
214
214
  ```
215
215
  console.log("Hello, Alice!");
216
216
  ``` This is useful when working with optional values from your IR:
217
217
  ```
218
- let code = ts_template! {
219
- {#if let Some(default_val) = field.default_value}
220
- this.@{field.name} = @{default_val};
221
- {:else}
222
- this.@{field.name} = undefined;
223
- {/if}
224
- };
218
+ let code = ts_template! &#123;
219
+ &#123;#if let Some(default_val) = field.default_value&#125;
220
+ this.@&#123;field.name&#125; = @&#123;default_val&#125;;
221
+ &#123;:else&#125;
222
+ this.@&#123;field.name&#125; = undefined;
223
+ &#123;/if&#125;
224
+ &#125;;
225
225
  ``` ## Match Expressions: `{#match}`
226
226
  Use `match` for exhaustive pattern matching:
227
227
  ```
228
- enum Visibility { Public, Private, Protected }
228
+ enum Visibility &#123; Public, Private, Protected &#125;
229
229
  let visibility = Visibility::Public;
230
230
 
231
- let code = ts_template! {
232
- {#match visibility}
233
- {:case Visibility::Public}
231
+ let code = ts_template! &#123;
232
+ &#123;#match visibility&#125;
233
+ &#123;:case Visibility::Public&#125;
234
234
  public
235
- {:case Visibility::Private}
235
+ &#123;:case Visibility::Private&#125;
236
236
  private
237
- {:case Visibility::Protected}
237
+ &#123;:case Visibility::Protected&#125;
238
238
  protected
239
- {/match}
239
+ &#123;/match&#125;
240
240
  field: string;
241
- };
241
+ &#125;;
242
242
  ``` **Generates:**
243
243
  ```
244
244
  public field: string;
245
245
  ``` ### Match with Value Extraction
246
246
  ```
247
- let result: Result<i32, &str> = Ok(42);
247
+ let result: Result&#x3C;i32, &#x26;str> = Ok(42);
248
248
 
249
- let code = ts_template! {
250
- const value = {#match result}
251
- {:case Ok(val)}
252
- @{val}
253
- {:case Err(msg)}
254
- throw new Error("@{msg}")
255
- {/match};
256
- };
249
+ let code = ts_template! &#123;
250
+ const value = &#123;#match result&#125;
251
+ &#123;:case Ok(val)&#125;
252
+ @&#123;val&#125;
253
+ &#123;:case Err(msg)&#125;
254
+ throw new Error("@&#123;msg&#125;")
255
+ &#123;/match&#125;;
256
+ &#125;;
257
257
  ``` ### Match with Wildcard
258
258
  ```
259
259
  let count = 5;
260
260
 
261
- let code = ts_template! {
262
- {#match count}
263
- {:case 0}
261
+ let code = ts_template! &#123;
262
+ &#123;#match count&#125;
263
+ &#123;:case 0&#125;
264
264
  console.log("none");
265
- {:case 1}
265
+ &#123;:case 1&#125;
266
266
  console.log("one");
267
- {:case _}
267
+ &#123;:case _&#125;
268
268
  console.log("many");
269
- {/match}
270
- };
269
+ &#123;/match&#125;
270
+ &#125;;
271
271
  ``` ## Iteration: `{#for}`
272
272
  ```
273
273
  let fields = vec!["name", "email", "age"];
274
274
 
275
- let code = ts_template! {
276
- function toJSON() {
277
- const result = {};
278
- {#for field in fields}
279
- result.@{field} = this.@{field};
280
- {/for}
275
+ let code = ts_template! &#123;
276
+ function toJSON() &#123;
277
+ const result = &#123;&#125;;
278
+ &#123;#for field in fields&#125;
279
+ result.@&#123;field&#125; = this.@&#123;field&#125;;
280
+ &#123;/for&#125;
281
281
  return result;
282
- }
283
- };
282
+ &#125;
283
+ &#125;;
284
284
  ``` **Generates:**
285
285
  ```
286
- function toJSON() {
287
- const result = {};
286
+ function toJSON() &#123;
287
+ const result = &#123;&#125;;
288
288
  result.name = this.name;
289
289
  result.email = this.email;
290
290
  result.age = this.age;
291
291
  return result;
292
- }
292
+ &#125;
293
293
  ``` ### Tuple Destructuring in Loops
294
294
  ```
295
295
  let items = vec![("user", "User"), ("post", "Post")];
296
296
 
297
- let code = ts_template! {
298
- {#for (key, class_name) in items}
299
- const @{key} = new @{class_name}();
300
- {/for}
301
- };
297
+ let code = ts_template! &#123;
298
+ &#123;#for (key, class_name) in items&#125;
299
+ const @&#123;key&#125; = new @&#123;class_name&#125;();
300
+ &#123;/for&#125;
301
+ &#125;;
302
302
  ``` ### Nested Iterations
303
303
  ```
304
304
  let classes = vec![
@@ -306,40 +306,40 @@ let classes = vec![
306
306
  ("Post", vec!["title", "content"]),
307
307
  ];
308
308
 
309
- ts_template! {
310
- {#for (class_name, fields) in classes}
311
- @{class_name}.prototype.toJSON = function() {
312
- return {
313
- {#for field in fields}
314
- @{field}: this.@{field},
315
- {/for}
316
- };
317
- };
318
- {/for}
319
- }
309
+ ts_template! &#123;
310
+ &#123;#for (class_name, fields) in classes&#125;
311
+ @&#123;class_name&#125;.prototype.toJSON = function() &#123;
312
+ return &#123;
313
+ &#123;#for field in fields&#125;
314
+ @&#123;field&#125;: this.@&#123;field&#125;,
315
+ &#123;/for&#125;
316
+ &#125;;
317
+ &#125;;
318
+ &#123;/for&#125;
319
+ &#125;
320
320
  ``` ## While Loops: `{#while}`
321
321
  Use `while` for loops that need to continue until a condition is false:
322
322
  ```
323
323
  let items = get_items();
324
324
  let mut idx = 0;
325
325
 
326
- let code = ts_template! {
327
- {$let mut i = 0}
328
- {#while i < items.len()}
329
- console.log("Item @{i}");
330
- {$do i += 1}
331
- {/while}
332
- };
326
+ let code = ts_template! &#123;
327
+ &#123;$let mut i = 0&#125;
328
+ &#123;#while i &#x3C; items.len()&#125;
329
+ console.log("Item @&#123;i&#125;");
330
+ &#123;$do i += 1&#125;
331
+ &#123;/while&#125;
332
+ &#125;;
333
333
  ``` ### While-Let Pattern Matching
334
334
  Use `while let` for iterating with pattern matching, similar to `if let`:
335
335
  ```
336
336
  let mut items = vec!["a", "b", "c"].into_iter();
337
337
 
338
- let code = ts_template! {
339
- {#while let Some(item) = items.next()}
340
- console.log("@{item}");
341
- {/while}
342
- };
338
+ let code = ts_template! &#123;
339
+ &#123;#while let Some(item) = items.next()&#125;
340
+ console.log("@&#123;item&#125;");
341
+ &#123;/while&#125;
342
+ &#125;;
343
343
  ``` **Generates:**
344
344
  ```
345
345
  console.log("a");
@@ -347,45 +347,45 @@ console.log("b");
347
347
  console.log("c");
348
348
  ``` This is especially useful when working with iterators or consuming optional values:
349
349
  ```
350
- let code = ts_template! {
351
- {#while let Some(next_field) = remaining_fields.pop()}
352
- result.@{next_field.name} = this.@{next_field.name};
353
- {/while}
354
- };
350
+ let code = ts_template! &#123;
351
+ &#123;#while let Some(next_field) = remaining_fields.pop()&#125;
352
+ result.@&#123;next_field.name&#125; = this.@&#123;next_field.name&#125;;
353
+ &#123;/while&#125;
354
+ &#125;;
355
355
  ``` ## Local Constants: `{$let}`
356
356
  Define local variables within the template scope:
357
357
  ```
358
358
  let items = vec![("user", "User"), ("post", "Post")];
359
359
 
360
- let code = ts_template! {
361
- {#for (key, class_name) in items}
362
- {$let upper = class_name.to_uppercase()}
363
- console.log("Processing @{upper}");
364
- const @{key} = new @{class_name}();
365
- {/for}
366
- };
360
+ let code = ts_template! &#123;
361
+ &#123;#for (key, class_name) in items&#125;
362
+ &#123;$let upper = class_name.to_uppercase()&#125;
363
+ console.log("Processing @&#123;upper&#125;");
364
+ const @&#123;key&#125; = new @&#123;class_name&#125;();
365
+ &#123;/for&#125;
366
+ &#125;;
367
367
  ``` This is useful for computing derived values inside loops without cluttering the Rust code.
368
368
  ## Mutable Variables: `{$let mut}`
369
369
  When you need to modify a variable within the template (e.g., in a `while` loop), use `{$let mut}`:
370
370
  ```
371
- let code = ts_template! {
372
- {$let mut count = 0}
373
- {#for item in items}
374
- console.log("Item @{count}: @{item}");
375
- {$do count += 1}
376
- {/for}
377
- console.log("Total: @{count}");
378
- };
371
+ let code = ts_template! &#123;
372
+ &#123;$let mut count = 0&#125;
373
+ &#123;#for item in items&#125;
374
+ console.log("Item @&#123;count&#125;: @&#123;item&#125;");
375
+ &#123;$do count += 1&#125;
376
+ &#123;/for&#125;
377
+ console.log("Total: @&#123;count&#125;");
378
+ &#125;;
379
379
  ``` ## Side Effects: `{$do}`
380
380
  Execute an expression for its side effects without producing output. This is commonly used with mutable variables:
381
381
  ```
382
- let code = ts_template! {
383
- {$let mut results: Vec<String> = Vec::new()}
384
- {#for field in fields}
385
- {$do results.push(format!("this.{}", field))}
386
- {/for}
387
- return [@{results.join(", ")}];
388
- };
382
+ let code = ts_template! &#123;
383
+ &#123;$let mut results: Vec&#x3C;String> = Vec::new()&#125;
384
+ &#123;#for field in fields&#125;
385
+ &#123;$do results.push(format!("this.&#123;&#125;", field))&#125;
386
+ &#123;/for&#125;
387
+ return [@&#123;results.join(", ")&#125;];
388
+ &#125;;
389
389
  ``` Common uses for `{$do}`:
390
390
  - Incrementing counters: `{$do i += 1}`
391
391
  - Building collections: `{$do vec.push(item)}`
@@ -395,69 +395,69 @@ let code = ts_template! {
395
395
  Inject another TsStream into your template, preserving both its source code and runtime patches (like imports added via `add_import()`):
396
396
  ```
397
397
  // Create a helper method with its own import
398
- let mut helper = body! {
399
- validateEmail(email: string): boolean {
398
+ let mut helper = body! &#123;
399
+ validateEmail(email: string): boolean &#123;
400
400
  return Result.ok(true);
401
- }
402
- };
401
+ &#125;
402
+ &#125;;
403
403
  helper.add_import("Result", "macroforge/utils");
404
404
 
405
405
  // Inject the helper into the main template
406
- let result = body! {
407
- {$typescript helper}
406
+ let result = body! &#123;
407
+ &#123;$typescript helper&#125;
408
408
 
409
- process(data: Record<string, unknown>): void {
409
+ process(data: Record&#x3C;string, unknown>): void &#123;
410
410
  // ...
411
- }
412
- };
411
+ &#125;
412
+ &#125;;
413
413
  // result now includes helper's source AND its Result import
414
414
  ``` This is essential for composing multiple macro outputs while preserving imports and patches:
415
415
  ```
416
- let extra_methods = if include_validation {
417
- Some(body! {
418
- validate(): boolean { return true; }
419
- })
420
- } else {
416
+ let extra_methods = if include_validation &#123;
417
+ Some(body! &#123;
418
+ validate(): boolean &#123; return true; &#125;
419
+ &#125;)
420
+ &#125; else &#123;
421
421
  None
422
- };
422
+ &#125;;
423
423
 
424
- body! {
425
- mainMethod(): void {}
424
+ body! &#123;
425
+ mainMethod(): void &#123;&#125;
426
426
 
427
- {#if let Some(methods) = extra_methods}
428
- {$typescript methods}
429
- {/if}
430
- }
427
+ &#123;#if let Some(methods) = extra_methods&#125;
428
+ &#123;$typescript methods&#125;
429
+ &#123;/if&#125;
430
+ &#125;
431
431
  ``` ## Escape Syntax
432
432
  If you need a literal `@{` in your output (not interpolation), use `@@{`:
433
433
  ```
434
- ts_template! {
435
- // This outputs a literal @{foo}
436
- const example = "Use @@{foo} for templates";
437
- }
434
+ ts_template! &#123;
435
+ // This outputs a literal @&#123;foo&#125;
436
+ const example = "Use @@&#123;foo&#125; for templates";
437
+ &#125;
438
438
  ``` **Generates:**
439
439
  ```
440
- // This outputs a literal @{foo}
441
- const example = "Use @{foo} for templates";
440
+ // This outputs a literal @&#123;foo&#125;
441
+ const example = "Use @&#123;foo&#125; for templates";
442
442
  ``` ## Complete Example: JSON Derive Macro
443
443
  Here's a comparison showing how `ts_template!` simplifies code generation:
444
444
  ### Before (Manual AST Building)
445
445
  ```
446
- pub fn derive_json_macro(input: TsStream) -> MacroResult {
446
+ pub fn derive_json_macro(input: TsStream) -> MacroResult &#123;
447
447
  let input = parse_ts_macro_input!(input as DeriveInput);
448
448
 
449
- match &input.data {
450
- Data::Class(class) => {
449
+ match &#x26;input.data &#123;
450
+ Data::Class(class) => &#123;
451
451
  let class_name = input.name();
452
452
 
453
- let mut body_stmts = vec![ts_quote!( const result = {}; as Stmt )];
453
+ let mut body_stmts = vec![ts_quote!( const result = &#123;&#125;; as Stmt )];
454
454
 
455
- for field_name in class.field_names() {
455
+ for field_name in class.field_names() &#123;
456
456
  body_stmts.push(ts_quote!(
457
- result.$(ident!("{}", field_name)) = this.$(ident!("{}", field_name));
457
+ result.$(ident!("&#123;&#125;", field_name)) = this.$(ident!("&#123;&#125;", field_name));
458
458
  as Stmt
459
459
  ));
460
- }
460
+ &#125;
461
461
 
462
462
  body_stmts.push(ts_quote!( return result; as Stmt ));
463
463
 
@@ -468,33 +468,33 @@ pub fn derive_json_macro(input: TsStream) -> MacroResult {
468
468
  );
469
469
 
470
470
  // ...
471
- }
472
- }
473
- }
471
+ &#125;
472
+ &#125;
473
+ &#125;
474
474
  ``` ### After (With ts_template!)
475
475
  ```
476
- pub fn derive_json_macro(input: TsStream) -> MacroResult {
476
+ pub fn derive_json_macro(input: TsStream) -> MacroResult &#123;
477
477
  let input = parse_ts_macro_input!(input as DeriveInput);
478
478
 
479
- match &input.data {
480
- Data::Class(class) => {
479
+ match &#x26;input.data &#123;
480
+ Data::Class(class) => &#123;
481
481
  let class_name = input.name();
482
482
  let fields = class.field_names();
483
483
 
484
- let runtime_code = ts_template! {
485
- @{class_name}.prototype.toJSON = function() {
486
- const result = {};
487
- {#for field in fields}
488
- result.@{field} = this.@{field};
489
- {/for}
484
+ let runtime_code = ts_template! &#123;
485
+ @&#123;class_name&#125;.prototype.toJSON = function() &#123;
486
+ const result = &#123;&#125;;
487
+ &#123;#for field in fields&#125;
488
+ result.@&#123;field&#125; = this.@&#123;field&#125;;
489
+ &#123;/for&#125;
490
490
  return result;
491
- };
492
- };
491
+ &#125;;
492
+ &#125;;
493
493
 
494
494
  // ...
495
- }
496
- }
497
- }
495
+ &#125;
496
+ &#125;
497
+ &#125;
498
498
  ``` ## How It Works
499
499
  1. **Compile-Time:** The template is parsed during macro expansion
500
500
  2. **String Building:** Generates Rust code that builds a TypeScript string at runtime
@@ -504,25 +504,25 @@ pub fn derive_json_macro(input: TsStream) -> MacroResult {
504
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
505
  ```
506
506
  Failed to parse generated TypeScript:
507
- User.prototype.toJSON = function( {
508
- return {};
509
- }
507
+ User.prototype.toJSON = function( &#123;
508
+ return &#123;&#125;;
509
+ &#125;
510
510
  ``` This shows you exactly what was generated, making debugging easy!
511
511
  ## Nesting and Regular TypeScript
512
512
  You can mix template syntax with regular TypeScript. Braces `{}` are recognized as either:
513
513
  - **Template tags** if they start with `#`, `$`, `:`, or `/`
514
514
  - **Regular TypeScript blocks** otherwise
515
515
  ```
516
- ts_template! {
517
- const config = {
518
- {#if use_strict}
516
+ ts_template! &#123;
517
+ const config = &#123;
518
+ &#123;#if use_strict&#125;
519
519
  strict: true,
520
- {:else}
520
+ &#123;:else&#125;
521
521
  strict: false,
522
- {/if}
522
+ &#123;/if&#125;
523
523
  timeout: 5000
524
- };
525
- }
524
+ &#125;;
525
+ &#125;
526
526
  ``` ## Comparison with Alternatives
527
527
  | Approach | Pros | Cons |
528
528
  | --- | --- | --- |