@macroforge/mcp-server 0.1.40 → 0.1.49

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