@macroforge/mcp-server 0.1.42 → 0.1.50
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +22 -0
- package/docs/BOOK.md +165 -0
- package/docs/api/api-overview.md +67 -48
- package/docs/api/expand-sync.md +88 -53
- package/docs/api/native-plugin.md +121 -71
- package/docs/api/position-mapper.md +115 -55
- package/docs/api/transform-sync.md +86 -60
- package/docs/builtin-macros/clone.md +0 -20
- package/docs/builtin-macros/debug.md +0 -23
- package/docs/builtin-macros/default.md +1 -40
- package/docs/builtin-macros/deserialize/example.md +8 -1416
- package/docs/builtin-macros/deserialize.md +8 -1416
- package/docs/builtin-macros/hash.md +0 -42
- package/docs/builtin-macros/macros-overview/detailed-documentation.md +13 -0
- package/docs/builtin-macros/macros-overview/enum-support.md +30 -0
- package/docs/builtin-macros/macros-overview/interface-support.md +28 -0
- package/docs/builtin-macros/macros-overview/overview.md +36 -0
- package/docs/builtin-macros/macros-overview/type-alias-support.md +62 -0
- package/docs/builtin-macros/macros-overview.md +171 -130
- package/docs/builtin-macros/ord.md +0 -25
- package/docs/builtin-macros/partial-eq.md +0 -84
- package/docs/builtin-macros/partial-ord.md +2 -32
- package/docs/builtin-macros/serialize.md +2 -62
- package/docs/concepts/architecture.md +125 -48
- package/docs/concepts/derive-system/built-in-vs-custom-macros.md +13 -0
- package/docs/concepts/derive-system/overview.md +200 -0
- package/docs/concepts/derive-system.md +157 -104
- package/docs/concepts/how-macros-work.md +98 -47
- package/docs/custom-macros/custom-overview.md +79 -57
- package/docs/custom-macros/rust-setup.md +138 -99
- package/docs/custom-macros/ts-macro-derive/accessing-field-data.md +40 -31
- package/docs/custom-macros/ts-macro-derive/adding-imports.md +14 -11
- package/docs/custom-macros/ts-macro-derive/attribute-options.md +20 -25
- package/docs/custom-macros/ts-macro-derive/complete-example.md +40 -38
- package/docs/custom-macros/ts-macro-derive/deriveinput-structure.md +49 -47
- package/docs/custom-macros/ts-macro-derive/function-signature.md +12 -0
- package/docs/custom-macros/ts-macro-derive/overview.md +9 -7
- package/docs/custom-macros/ts-macro-derive/parsing-input.md +20 -18
- package/docs/custom-macros/ts-macro-derive/returning-errors.md +15 -13
- package/docs/custom-macros/ts-macro-derive.md +322 -228
- package/docs/custom-macros/ts-quote/backtick-template-literals.md +19 -7
- package/docs/custom-macros/ts-quote/comments-and.md +56 -22
- package/docs/custom-macros/ts-quote/complete-example-json-derive-macro.md +89 -98
- package/docs/custom-macros/ts-quote/conditionals-ifif.md +35 -29
- package/docs/custom-macros/ts-quote/identifier-concatenation-content.md +30 -22
- package/docs/custom-macros/ts-quote/iteration-for.md +48 -40
- package/docs/custom-macros/ts-quote/local-constants-let.md +23 -21
- package/docs/custom-macros/ts-quote/match-expressions-match.md +46 -38
- package/docs/custom-macros/ts-quote/overview.md +5 -10
- package/docs/custom-macros/ts-quote/pattern-matching-iflet.md +39 -0
- package/docs/custom-macros/ts-quote/quick-reference.md +50 -129
- package/docs/custom-macros/ts-quote/side-effects-do.md +13 -78
- package/docs/custom-macros/ts-quote/string-interpolation-textexpr.md +36 -0
- package/docs/custom-macros/ts-quote/tsstream-injection-typescript.md +43 -35
- package/docs/custom-macros/ts-quote/while-loops-while.md +31 -23
- package/docs/custom-macros/ts-quote.md +800 -520
- package/docs/getting-started/first-macro.md +98 -71
- package/docs/getting-started/installation.md +109 -65
- package/docs/integration/cli.md +214 -105
- package/docs/integration/configuration.md +115 -72
- package/docs/integration/integration-overview.md +55 -18
- package/docs/integration/mcp-server.md +84 -43
- package/docs/integration/svelte-preprocessor.md +183 -126
- package/docs/integration/typescript-plugin.md +101 -53
- package/docs/integration/vite-plugin.md +116 -76
- package/docs/language-servers/ls-overview.md +37 -21
- package/docs/language-servers/svelte.md +69 -38
- package/docs/language-servers/zed.md +81 -44
- package/docs/roadmap/roadmap.md +75 -53
- package/docs/sections.json +1 -242
- package/package.json +27 -28
|
@@ -1,58 +1,66 @@
|
|
|
1
|
-
## Match Expressions:
|
|
1
|
+
## Match Expressions: `{#match}`
|
|
2
2
|
|
|
3
3
|
Use `match` for exhaustive pattern matching:
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
let
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
5
|
+
Rust
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
enum Visibility { Public, Private, Protected }
|
|
9
|
+
let visibility = Visibility::Public;
|
|
10
|
+
|
|
11
|
+
let code = ts_template! {
|
|
12
|
+
{#match visibility}
|
|
13
|
+
{:case Visibility::Public}
|
|
14
|
+
public
|
|
15
|
+
{:case Visibility::Private}
|
|
16
|
+
private
|
|
17
|
+
{:case Visibility::Protected}
|
|
18
|
+
protected
|
|
19
|
+
{/match}
|
|
20
|
+
field: string;
|
|
19
21
|
};
|
|
20
22
|
```
|
|
21
23
|
|
|
22
24
|
**Generates:**
|
|
23
25
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
+
TypeScript
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
public field: string;
|
|
26
30
|
```
|
|
27
31
|
|
|
28
32
|
### Match with Value Extraction
|
|
29
33
|
|
|
30
|
-
|
|
31
|
-
let result: Result<i32, &str> = Ok(42);
|
|
34
|
+
Rust
|
|
32
35
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
36
|
+
```
|
|
37
|
+
let result: Result<i32, &str> = Ok(42);
|
|
38
|
+
|
|
39
|
+
let code = ts_template! {
|
|
40
|
+
const value = {#match result}
|
|
41
|
+
{:case Ok(val)}
|
|
42
|
+
@{val}
|
|
43
|
+
{:case Err(msg)}
|
|
44
|
+
throw new Error("@{msg}")
|
|
45
|
+
{/match};
|
|
40
46
|
};
|
|
41
47
|
```
|
|
42
48
|
|
|
43
49
|
### Match with Wildcard
|
|
44
50
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
let
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
51
|
+
Rust
|
|
52
|
+
|
|
53
|
+
```
|
|
54
|
+
let count = 5;
|
|
55
|
+
|
|
56
|
+
let code = ts_template! {
|
|
57
|
+
{#match count}
|
|
58
|
+
{:case 0}
|
|
59
|
+
console.log("none");
|
|
60
|
+
{:case 1}
|
|
61
|
+
console.log("one");
|
|
62
|
+
{:case _}
|
|
63
|
+
console.log("many");
|
|
64
|
+
{/match}
|
|
57
65
|
};
|
|
58
66
|
```
|
|
@@ -1,15 +1,10 @@
|
|
|
1
1
|
# Template Syntax
|
|
2
2
|
|
|
3
|
-
|
|
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
4
|
|
|
5
5
|
## Available Macros
|
|
6
6
|
|
|
7
|
-
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
| `body!`
|
|
14
|
-
| Class body members
|
|
15
|
-
| Methods and properties
|
|
7
|
+
| Macro | Output | Use Case |
|
|
8
|
+
| -------------- | ------------------- | ----------------------- |
|
|
9
|
+
| `ts_template!` | Any TypeScript code | General code generation |
|
|
10
|
+
| `body!` | Class body members | Methods and properties |
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
## Pattern Matching: `{#if let}`
|
|
2
|
+
|
|
3
|
+
Use `if let` for pattern matching on `Option`, `Result`, or other Rust enums:
|
|
4
|
+
|
|
5
|
+
Rust
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
let maybe_name: Option<&str> = Some("Alice");
|
|
9
|
+
|
|
10
|
+
let code = ts_template! {
|
|
11
|
+
{#if let Some(name) = maybe_name}
|
|
12
|
+
console.log("Hello, @{name}!");
|
|
13
|
+
{:else}
|
|
14
|
+
console.log("Hello, anonymous!");
|
|
15
|
+
{/if}
|
|
16
|
+
};
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
**Generates:**
|
|
20
|
+
|
|
21
|
+
TypeScript
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
console.log("Hello, Alice!");
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
This is useful when working with optional values from your IR:
|
|
28
|
+
|
|
29
|
+
Rust
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
let code = ts_template! {
|
|
33
|
+
{#if let Some(default_val) = field.default_value}
|
|
34
|
+
this.@{field.name} = @{default_val};
|
|
35
|
+
{:else}
|
|
36
|
+
this.@{field.name} = undefined;
|
|
37
|
+
{/if}
|
|
38
|
+
};
|
|
39
|
+
```
|
|
@@ -1,131 +1,52 @@
|
|
|
1
1
|
## Quick Reference
|
|
2
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
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
| <code
|
|
57
|
-
>{#if cond}...{:else}...{/if}</code
|
|
58
|
-
></td
|
|
59
|
-
>
|
|
60
|
-
<td>Conditional with else
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
| <code
|
|
65
|
-
>{#if a}...{:else if
|
|
66
|
-
b}...{:else}...{/if}</code
|
|
67
|
-
></td
|
|
68
|
-
>
|
|
69
|
-
<td>Full if/else-if/else chain
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
| <code
|
|
74
|
-
>{#if let pattern = expr}...{/if}</code
|
|
75
|
-
></td
|
|
76
|
-
>
|
|
77
|
-
<td>Pattern matching if-let
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
| <code
|
|
82
|
-
>{#match expr}{:case
|
|
83
|
-
pattern}...{/match}</code
|
|
84
|
-
></td
|
|
85
|
-
>
|
|
86
|
-
<td>Match expression with case arms
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
| <code>{#for item in list}...{/for}</code
|
|
91
|
-
></td
|
|
92
|
-
>
|
|
93
|
-
<td>Iterate over a collection
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
| `{#while cond}...{/while}`
|
|
98
|
-
| While loop
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
| <code
|
|
103
|
-
>{#while let pattern = expr}...{/while}</code
|
|
104
|
-
></td
|
|
105
|
-
>
|
|
106
|
-
<td>While-let pattern matching loop
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
| `{$let name = expr}`
|
|
111
|
-
| Define a local constant
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
| `{$let mut name = expr}`
|
|
116
|
-
| Define a mutable local variable
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
| `{$do expr}`
|
|
121
|
-
| Execute a side-effectful expression
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
| `{$typescript stream}`
|
|
126
|
-
<td
|
|
127
|
-
>Inject a TsStream, preserving its source and runtime_patches
|
|
128
|
-
(imports)</td
|
|
129
|
-
>
|
|
130
|
-
|
|
131
|
-
**Note:** A single `@` not followed by `{` passes through unchanged (e.g., `email@domain.com` works as expected).
|
|
3
|
+
| Syntax | Description |
|
|
4
|
+
| -------------------------------------------------------------- | ----------------------------------------------------------------------------- |
|
|
5
|
+
| `@{expr}` | Interpolate a Rust expression (adds space after) |
|
|
6
|
+
| `{| content |}` | Ident block: concatenates without spaces (e.g., `{|get@{name}|}` → `getUser`) |
|
|
7
|
+
| `{> "comment" <}` | Block comment: outputs `/* comment */` (string preserves whitespace) |
|
|
8
|
+
| `{>> "doc" <<}` | Doc comment: outputs `/** doc */` (string preserves whitespace) |
|
|
9
|
+
| `@@{` | Escape for literal `@{` (e.g., `"@@{foo}"` → `@{foo}`) |
|
|
10
|
+
| `"text @{expr}"` | String interpolation (auto-detected) |
|
|
11
|
+
| `"'^template ${js}^'"` | JS backtick template literal (outputs `` `template ${js}` ``) |
|
|
12
|
+
| `{#if cond}...{/if}` | Conditional block |
|
|
13
|
+
| `{#if cond}...{:else}...{/if}` | Conditional with else |
|
|
14
|
+
| `{#if a}...{:else if b}...{:else}...{/if}` | Full if/else-if/else chain |
|
|
15
|
+
| `{#if let pattern = expr}...{/if}` | Pattern matching if-let |
|
|
16
|
+
| `{#match expr}{:case pattern}...{/match}` | Match expression with case arms |
|
|
17
|
+
| `{#for item in list}...{/for}` | Iterate over a collection |
|
|
18
|
+
| `{#while cond}...{/while}` | While loop |
|
|
19
|
+
| `{#while let pattern = expr}...{/while}` | While-let pattern matching loop |
|
|
20
|
+
| `{$let name = expr}` | Define a local constant |
|
|
21
|
+
| `{$let mut name = expr}` | Define a mutable local variable |
|
|
22
|
+
| `{$do expr}` | Execute a side-effectful expression |
|
|
23
|
+
| `{$typescript stream}` | Inject a TsStream, preserving its source and runtime\_patches (imports) |
|
|
24
|
+
|
|
25
|
+
**Note:** A single `@` not followed by `{` passes through unchanged (e.g., `email@domain.com` works as expected).
|
|
26
|
+
|
|
27
|
+
## Interpolation: `@{expr}`
|
|
28
|
+
|
|
29
|
+
Insert Rust expressions into the generated TypeScript:
|
|
30
|
+
|
|
31
|
+
Rust
|
|
32
|
+
|
|
33
|
+
```
|
|
34
|
+
let class_name = "User";
|
|
35
|
+
let method = "toString";
|
|
36
|
+
|
|
37
|
+
let code = ts_template! {
|
|
38
|
+
@{class_name}.prototype.@{method} = function() {
|
|
39
|
+
return "User instance";
|
|
40
|
+
};
|
|
41
|
+
};
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
**Generates:**
|
|
45
|
+
|
|
46
|
+
TypeScript
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
User.prototype.toString = function () {
|
|
50
|
+
return "User instance";
|
|
51
|
+
};
|
|
52
|
+
```
|
|
@@ -1,87 +1,22 @@
|
|
|
1
|
-
## Side Effects:
|
|
1
|
+
## Side Effects: `{$do}`
|
|
2
2
|
|
|
3
3
|
Execute an expression for its side effects without producing output. This is commonly used with mutable variables:
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
let code = ts_template! {
|
|
7
|
-
{$let mut results: Vec<String> = Vec::new()}
|
|
8
|
-
{#for field in fields}
|
|
9
|
-
{$do results.push(format!("this.{}", field))}
|
|
10
|
-
{/for}
|
|
11
|
-
return [@{results.join(", ")}];
|
|
12
|
-
};
|
|
13
|
-
```
|
|
14
|
-
|
|
15
|
-
Common uses for `{$do}`:
|
|
16
|
-
|
|
17
|
-
- Incrementing counters: `{$do i += 1}`
|
|
18
|
-
|
|
19
|
-
- Building collections: `{$do vec.push(item)}`
|
|
20
|
-
|
|
21
|
-
- Setting flags: `{$do found = true}`
|
|
22
|
-
|
|
23
|
-
- Any mutating operation
|
|
24
|
-
|
|
25
|
-
<h2 id="typescript-injection">
|
|
26
|
-
TsStream Injection: `{$typescript}`
|
|
27
|
-
</h2>
|
|
28
|
-
|
|
29
|
-
Inject another TsStream into your template, preserving both its source code and runtime patches (like imports added via `add_import()`):
|
|
30
|
-
|
|
31
|
-
```rust
|
|
32
|
-
// Create a helper method with its own import
|
|
33
|
-
let mut helper = body! {
|
|
34
|
-
validateEmail(email: string): boolean {
|
|
35
|
-
return Result.ok(true);
|
|
36
|
-
}
|
|
37
|
-
};
|
|
38
|
-
helper.add_import("Result", "macroforge/utils");
|
|
5
|
+
Rust
|
|
39
6
|
|
|
40
|
-
// Inject the helper into the main template
|
|
41
|
-
let result = body! {
|
|
42
|
-
{$typescript helper}
|
|
43
|
-
|
|
44
|
-
process(data: Record<string, unknown>): void {
|
|
45
|
-
// ...
|
|
46
|
-
}
|
|
47
|
-
};
|
|
48
|
-
// result now includes helper's source AND its Result import
|
|
49
7
|
```
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
validate(): boolean { return true; }
|
|
57
|
-
})
|
|
58
|
-
} else {
|
|
59
|
-
None
|
|
8
|
+
let code = ts_template! {
|
|
9
|
+
{$let mut results: Vec<String> = Vec::new()}
|
|
10
|
+
{#for field in fields}
|
|
11
|
+
{$do results.push(format!("this.{}", field))}
|
|
12
|
+
{/for}
|
|
13
|
+
return [@{results.join(", ")}];
|
|
60
14
|
};
|
|
61
|
-
|
|
62
|
-
body! {
|
|
63
|
-
mainMethod(): void {}
|
|
64
|
-
|
|
65
|
-
{#if let Some(methods) = extra_methods}
|
|
66
|
-
{$typescript methods}
|
|
67
|
-
{/if}
|
|
68
|
-
}
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
## Escape Syntax
|
|
72
|
-
|
|
73
|
-
If you need a literal `@{` in your output (not interpolation), use `@@{`:
|
|
74
|
-
|
|
75
|
-
```rust
|
|
76
|
-
ts_template! {
|
|
77
|
-
// This outputs a literal @{foo}
|
|
78
|
-
const example = "Use @@{foo} for templates";
|
|
79
|
-
}
|
|
80
15
|
```
|
|
81
16
|
|
|
82
|
-
|
|
17
|
+
Common uses for `{$do}`:
|
|
83
18
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
19
|
+
* Incrementing counters: `{$do i += 1}`
|
|
20
|
+
* Building collections: `{$do vec.push(item)}`
|
|
21
|
+
* Setting flags: `{$do found = true}`
|
|
22
|
+
* Any mutating operation
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
## String Interpolation: `"text @{expr}"`
|
|
2
|
+
|
|
3
|
+
Interpolation works automatically inside string literals - no `format!()` needed:
|
|
4
|
+
|
|
5
|
+
Rust
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
let name = "World";
|
|
9
|
+
let count = 42;
|
|
10
|
+
|
|
11
|
+
let code = ts_template! {
|
|
12
|
+
console.log("Hello @{name}!");
|
|
13
|
+
console.log("Count: @{count}, doubled: @{count * 2}");
|
|
14
|
+
};
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
**Generates:**
|
|
18
|
+
|
|
19
|
+
TypeScript
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
console.log("Hello World!");
|
|
23
|
+
console.log("Count: 42, doubled: 84");
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
This also works with method calls and complex expressions:
|
|
27
|
+
|
|
28
|
+
Rust
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
let field = "username";
|
|
32
|
+
|
|
33
|
+
let code = ts_template! {
|
|
34
|
+
throw new Error("Invalid @{field.to_uppercase()}");
|
|
35
|
+
};
|
|
36
|
+
```
|
|
@@ -1,61 +1,69 @@
|
|
|
1
|
-
## TsStream Injection:
|
|
1
|
+
## TsStream Injection: `{$typescript}`
|
|
2
2
|
|
|
3
3
|
Inject another TsStream into your template, preserving both its source code and runtime patches (like imports added via `add_import()`):
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
5
|
+
Rust
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
// Create a helper method with its own import
|
|
9
|
+
let mut helper = body! {
|
|
10
|
+
validateEmail(email: string): boolean {
|
|
11
|
+
return Result.ok(true);
|
|
12
|
+
}
|
|
11
13
|
};
|
|
12
|
-
helper.add_import("Result",
|
|
14
|
+
helper.add_import("Result", "macroforge/utils");
|
|
13
15
|
|
|
14
|
-
//
|
|
15
|
-
let
|
|
16
|
-
|
|
16
|
+
// Inject the helper into the main template
|
|
17
|
+
let result = body! {
|
|
18
|
+
{$typescript helper}
|
|
17
19
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
20
|
+
process(data: Record<string, unknown>): void {
|
|
21
|
+
// ...
|
|
22
|
+
}
|
|
21
23
|
};
|
|
22
|
-
//
|
|
24
|
+
// result now includes helper's source AND its Result import
|
|
23
25
|
```
|
|
24
26
|
|
|
25
27
|
This is essential for composing multiple macro outputs while preserving imports and patches:
|
|
26
28
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
}
|
|
33
|
-
|
|
29
|
+
Rust
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
let extra_methods = if include_validation {
|
|
33
|
+
Some(body! {
|
|
34
|
+
validate(): boolean { return true; }
|
|
35
|
+
})
|
|
36
|
+
} else {
|
|
37
|
+
None
|
|
34
38
|
};
|
|
35
39
|
|
|
36
|
-
body!
|
|
37
|
-
|
|
40
|
+
body! {
|
|
41
|
+
mainMethod(): void {}
|
|
38
42
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
43
|
+
{#if let Some(methods) = extra_methods}
|
|
44
|
+
{$typescript methods}
|
|
45
|
+
{/if}
|
|
42
46
|
}
|
|
43
47
|
```
|
|
44
48
|
|
|
45
49
|
## Escape Syntax
|
|
46
50
|
|
|
47
|
-
If you need a literal
|
|
51
|
+
If you need a literal `@{` in your output (not interpolation), use `@@{`:
|
|
48
52
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
+
Rust
|
|
54
|
+
|
|
55
|
+
```
|
|
56
|
+
ts_template! {
|
|
57
|
+
// This outputs a literal @{foo}
|
|
58
|
+
const example = "Use @@{foo} for templates";
|
|
53
59
|
}
|
|
54
60
|
```
|
|
55
61
|
|
|
56
62
|
**Generates:**
|
|
57
63
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
64
|
+
TypeScript
|
|
65
|
+
|
|
66
|
+
```
|
|
67
|
+
// This outputs a literal @{foo}
|
|
68
|
+
const example = "Use @{foo} for templates";
|
|
61
69
|
```
|
|
@@ -1,37 +1,43 @@
|
|
|
1
|
-
## While Loops:
|
|
1
|
+
## While Loops: `{#while}`
|
|
2
2
|
|
|
3
3
|
Use `while` for loops that need to continue until a condition is false:
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
let items = get_items();
|
|
7
|
-
let mut idx = 0;
|
|
5
|
+
Rust
|
|
8
6
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
7
|
+
```
|
|
8
|
+
let items = get_items();
|
|
9
|
+
let mut idx = 0;
|
|
10
|
+
|
|
11
|
+
let code = ts_template! {
|
|
12
|
+
{$let mut i = 0}
|
|
13
|
+
{#while i < items.len()}
|
|
14
|
+
console.log("Item @{i}");
|
|
15
|
+
{$do i += 1}
|
|
16
|
+
{/while}
|
|
15
17
|
};
|
|
16
18
|
```
|
|
17
19
|
|
|
18
20
|
### While-Let Pattern Matching
|
|
19
21
|
|
|
20
|
-
Use `while
|
|
22
|
+
Use `while let` for iterating with pattern matching, similar to `if let`:
|
|
23
|
+
|
|
24
|
+
Rust
|
|
21
25
|
|
|
22
|
-
```
|
|
23
|
-
let
|
|
26
|
+
```
|
|
27
|
+
let mut items = vec!["a", "b", "c"].into_iter();
|
|
24
28
|
|
|
25
|
-
let
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
+
let code = ts_template! {
|
|
30
|
+
{#while let Some(item) = items.next()}
|
|
31
|
+
console.log("@{item}");
|
|
32
|
+
{/while}
|
|
29
33
|
};
|
|
30
34
|
```
|
|
31
35
|
|
|
32
36
|
**Generates:**
|
|
33
37
|
|
|
34
|
-
|
|
38
|
+
TypeScript
|
|
39
|
+
|
|
40
|
+
```
|
|
35
41
|
console.log("a");
|
|
36
42
|
console.log("b");
|
|
37
43
|
console.log("c");
|
|
@@ -39,10 +45,12 @@ console.log("c");
|
|
|
39
45
|
|
|
40
46
|
This is especially useful when working with iterators or consuming optional values:
|
|
41
47
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
48
|
+
Rust
|
|
49
|
+
|
|
50
|
+
```
|
|
51
|
+
let code = ts_template! {
|
|
52
|
+
{#while let Some(next_field) = remaining_fields.pop()}
|
|
53
|
+
result.@{next_field.name} = this.@{next_field.name};
|
|
54
|
+
{/while}
|
|
47
55
|
};
|
|
48
56
|
```
|