@macroforge/mcp-server 0.1.38 → 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.
- package/docs/api/api-overview.md +13 -13
- package/docs/api/expand-sync.md +8 -8
- package/docs/api/native-plugin.md +15 -15
- package/docs/api/position-mapper.md +6 -6
- package/docs/api/transform-sync.md +11 -11
- package/docs/builtin-macros/macros-overview.md +40 -40
- package/docs/concepts/architecture.md +2 -2
- package/docs/concepts/derive-system.md +5 -5
- package/docs/custom-macros/custom-overview.md +23 -23
- package/docs/custom-macros/rust-setup.md +31 -31
- package/docs/custom-macros/ts-macro-derive.md +107 -107
- package/docs/custom-macros/ts-quote.md +226 -226
- package/docs/getting-started/first-macro.md +2 -2
- package/docs/getting-started/installation.md +15 -15
- package/docs/integration/cli.md +9 -9
- package/docs/integration/configuration.md +16 -16
- package/docs/integration/mcp-server.md +6 -6
- package/docs/integration/svelte-preprocessor.md +40 -41
- package/docs/integration/typescript-plugin.md +13 -12
- package/docs/integration/vite-plugin.md +12 -12
- package/docs/language-servers/zed.md +1 -1
- package/package.json +2 -2
package/docs/api/api-overview.md
CHANGED
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
<span class="stats svelte-1c8t0id">52 exported items *Macroforge provides a programmatic API for expanding macros in TypeScript code.*
|
|
3
3
|
## Overview
|
|
4
4
|
```
|
|
5
|
-
import
|
|
5
|
+
import {
|
|
6
6
|
expandSync,
|
|
7
7
|
transformSync,
|
|
8
8
|
checkSyntax,
|
|
9
9
|
parseImportSources,
|
|
10
10
|
NativePlugin,
|
|
11
11
|
PositionMapper
|
|
12
|
-
|
|
12
|
+
} from "macroforge";
|
|
13
13
|
``` ## Core Functions
|
|
14
14
|
| Function | Description |
|
|
15
15
|
| --- | --- |
|
|
@@ -24,28 +24,28 @@ import {
|
|
|
24
24
|
| [`PositionMapper`](../docs/api/position-mapper) | Maps positions between original and expanded code |
|
|
25
25
|
## Quick Example
|
|
26
26
|
```
|
|
27
|
-
import
|
|
27
|
+
import { expandSync } from "macroforge";
|
|
28
28
|
|
|
29
|
-
const sourceCode =
|
|
29
|
+
const sourceCode = \`
|
|
30
30
|
/** @derive(Debug) */
|
|
31
|
-
class User
|
|
31
|
+
class User {
|
|
32
32
|
name: string;
|
|
33
|
-
constructor(name: string)
|
|
33
|
+
constructor(name: string) {
|
|
34
34
|
this.name = name;
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
\`;
|
|
38
38
|
|
|
39
|
-
const result = expandSync(sourceCode, "user.ts",
|
|
39
|
+
const result = expandSync(sourceCode, "user.ts", {
|
|
40
40
|
keepDecorators: false
|
|
41
|
-
|
|
41
|
+
});
|
|
42
42
|
|
|
43
43
|
console.log(result.code);
|
|
44
44
|
// Output: class with toString() method generated
|
|
45
45
|
|
|
46
|
-
if (result.diagnostics.length > 0)
|
|
46
|
+
if (result.diagnostics.length > 0) {
|
|
47
47
|
console.error("Errors:", result.diagnostics);
|
|
48
|
-
|
|
48
|
+
}
|
|
49
49
|
``` ## Detailed Reference
|
|
50
50
|
- [`expandSync()`](../docs/api/expand-sync) - Full options and return types
|
|
51
51
|
- [`transformSync()`](../docs/api/transform-sync) - Transform with source maps
|
package/docs/api/expand-sync.md
CHANGED
|
@@ -15,13 +15,13 @@ function expandSync(
|
|
|
15
15
|
| `options` | `ExpandOptions` | Optional configuration |
|
|
16
16
|
## ExpandOptions
|
|
17
17
|
```
|
|
18
|
-
interface ExpandOptions
|
|
18
|
+
interface ExpandOptions {
|
|
19
19
|
// Keep @derive decorators in output (default: false)
|
|
20
20
|
keepDecorators?: boolean;
|
|
21
|
-
|
|
21
|
+
}
|
|
22
22
|
``` ## ExpandResult
|
|
23
23
|
```
|
|
24
|
-
interface ExpandResult
|
|
24
|
+
interface ExpandResult {
|
|
25
25
|
// Transformed TypeScript code
|
|
26
26
|
code: string;
|
|
27
27
|
|
|
@@ -36,17 +36,17 @@ interface ExpandResult {
|
|
|
36
36
|
|
|
37
37
|
// Position mapping data for source maps
|
|
38
38
|
sourceMapping?: SourceMappingResult;
|
|
39
|
-
|
|
39
|
+
}
|
|
40
40
|
``` ## MacroDiagnostic
|
|
41
41
|
```
|
|
42
|
-
interface MacroDiagnostic
|
|
42
|
+
interface MacroDiagnostic {
|
|
43
43
|
message: string;
|
|
44
44
|
severity: "error" | "warning" | "info";
|
|
45
|
-
span:
|
|
45
|
+
span: {
|
|
46
46
|
start: number;
|
|
47
47
|
end: number;
|
|
48
|
-
|
|
49
|
-
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
50
|
``` ## Example
|
|
51
51
|
```
|
|
52
52
|
import { expandSync } from "macroforge";
|
|
@@ -13,10 +13,10 @@ processFile(
|
|
|
13
13
|
options?: ProcessFileOptions
|
|
14
14
|
): ExpandResult
|
|
15
15
|
``` ```
|
|
16
|
-
interface ProcessFileOptions
|
|
16
|
+
interface ProcessFileOptions {
|
|
17
17
|
// Cache key - if unchanged, returns cached result
|
|
18
18
|
version?: string;
|
|
19
|
-
|
|
19
|
+
}
|
|
20
20
|
``` ### getMapper()
|
|
21
21
|
Get the position mapper for a previously processed file:
|
|
22
22
|
```
|
|
@@ -39,36 +39,36 @@ setLogFile(path: string): void
|
|
|
39
39
|
const plugin = new NativePlugin();
|
|
40
40
|
|
|
41
41
|
// First call - performs expansion
|
|
42
|
-
const result1 = plugin.processFile("user.ts", code,
|
|
42
|
+
const result1 = plugin.processFile("user.ts", code, { version: "1" });
|
|
43
43
|
|
|
44
44
|
// Same version - returns cached result instantly
|
|
45
|
-
const result2 = plugin.processFile("user.ts", code,
|
|
45
|
+
const result2 = plugin.processFile("user.ts", code, { version: "1" });
|
|
46
46
|
|
|
47
47
|
// Different version - re-expands
|
|
48
|
-
const result3 = plugin.processFile("user.ts", newCode,
|
|
48
|
+
const result3 = plugin.processFile("user.ts", newCode, { version: "2" });
|
|
49
49
|
``` ## Example: Language Server Integration
|
|
50
50
|
```
|
|
51
|
-
import
|
|
51
|
+
import { NativePlugin } from "macroforge";
|
|
52
52
|
|
|
53
|
-
class MacroforgeLanguageService
|
|
53
|
+
class MacroforgeLanguageService {
|
|
54
54
|
private plugin = new NativePlugin();
|
|
55
55
|
|
|
56
|
-
processDocument(uri: string, content: string, version: number)
|
|
56
|
+
processDocument(uri: string, content: string, version: number) {
|
|
57
57
|
// Process with version-based caching
|
|
58
|
-
const result = this.plugin.processFile(uri, content,
|
|
58
|
+
const result = this.plugin.processFile(uri, content, {
|
|
59
59
|
version: String(version)
|
|
60
|
-
|
|
60
|
+
});
|
|
61
61
|
|
|
62
62
|
// Get mapper for position translation
|
|
63
63
|
const mapper = this.plugin.getMapper(uri);
|
|
64
64
|
|
|
65
|
-
return
|
|
66
|
-
|
|
65
|
+
return { result, mapper };
|
|
66
|
+
}
|
|
67
67
|
|
|
68
|
-
getSemanticDiagnostics(uri: string, diagnostics: Diagnostic[])
|
|
68
|
+
getSemanticDiagnostics(uri: string, diagnostics: Diagnostic[]) {
|
|
69
69
|
// Map positions from expanded to original
|
|
70
70
|
return this.plugin.mapDiagnostics(uri, diagnostics);
|
|
71
|
-
|
|
72
|
-
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
73
|
``` ## Thread Safety
|
|
74
74
|
The `NativePlugin` class is thread-safe and can be used from multiple async contexts. Each file is processed in an isolated thread with its own stack space.
|
|
@@ -2,16 +2,16 @@
|
|
|
2
2
|
*Bidirectional position mapper for translating between original and expanded source positions. This mapper enables IDE features like error reporting, go-to-definition, and hover to work correctly with macro-expanded code by translating positions between the original source (what the user wrote) and the expanded source (what the compiler sees).*
|
|
3
3
|
## Getting a Mapper
|
|
4
4
|
```
|
|
5
|
-
import
|
|
5
|
+
import { NativePlugin, PositionMapper } from "macroforge";
|
|
6
6
|
|
|
7
7
|
const plugin = new NativePlugin();
|
|
8
|
-
const result = plugin.processFile("user.ts", code,
|
|
8
|
+
const result = plugin.processFile("user.ts", code, { version: "1" });
|
|
9
9
|
|
|
10
10
|
// Get the mapper for this file
|
|
11
11
|
const mapper = plugin.getMapper("user.ts");
|
|
12
|
-
if (mapper)
|
|
12
|
+
if (mapper) {
|
|
13
13
|
// Use the mapper...
|
|
14
|
-
|
|
14
|
+
}
|
|
15
15
|
``` ## Methods
|
|
16
16
|
### isEmpty()
|
|
17
17
|
Check if the mapper has any mappings:
|
|
@@ -39,10 +39,10 @@ generatedBy(pos: number): string | null
|
|
|
39
39
|
```
|
|
40
40
|
mapSpanToOriginal(start: number, length: number): SpanResult | null
|
|
41
41
|
|
|
42
|
-
interface SpanResult
|
|
42
|
+
interface SpanResult {
|
|
43
43
|
start: number;
|
|
44
44
|
length: number;
|
|
45
|
-
|
|
45
|
+
}
|
|
46
46
|
``` ### mapSpanToExpanded()
|
|
47
47
|
Map a span from original to expanded code:
|
|
48
48
|
```
|
|
@@ -13,7 +13,7 @@ function transformSync(
|
|
|
13
13
|
| `filepath` | `string` | File path (used for error reporting) |
|
|
14
14
|
## TransformResult
|
|
15
15
|
```
|
|
16
|
-
interface TransformResult
|
|
16
|
+
interface TransformResult {
|
|
17
17
|
// Transformed TypeScript code
|
|
18
18
|
code: string;
|
|
19
19
|
|
|
@@ -25,7 +25,7 @@ interface TransformResult {
|
|
|
25
25
|
|
|
26
26
|
// Macro expansion metadata
|
|
27
27
|
metadata?: string;
|
|
28
|
-
|
|
28
|
+
}
|
|
29
29
|
``` ## Comparison with expandSync()
|
|
30
30
|
| Feature | `expandSync` | `transformSync` |
|
|
31
31
|
| --- | --- | --- |
|
|
@@ -35,29 +35,29 @@ interface TransformResult {
|
|
|
35
35
|
| Use Case | General purpose | Build tools |
|
|
36
36
|
## Example
|
|
37
37
|
```
|
|
38
|
-
import
|
|
38
|
+
import { transformSync } from "macroforge";
|
|
39
39
|
|
|
40
|
-
const sourceCode =
|
|
40
|
+
const sourceCode = \`
|
|
41
41
|
/** @derive(Debug) */
|
|
42
|
-
class User
|
|
42
|
+
class User {
|
|
43
43
|
name: string;
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
}
|
|
45
|
+
\`;
|
|
46
46
|
|
|
47
47
|
const result = transformSync(sourceCode, "user.ts");
|
|
48
48
|
|
|
49
49
|
console.log(result.code);
|
|
50
50
|
|
|
51
|
-
if (result.types)
|
|
51
|
+
if (result.types) {
|
|
52
52
|
// Write to .d.ts file
|
|
53
53
|
fs.writeFileSync("user.d.ts", result.types);
|
|
54
|
-
|
|
54
|
+
}
|
|
55
55
|
|
|
56
|
-
if (result.metadata)
|
|
56
|
+
if (result.metadata) {
|
|
57
57
|
// Parse and use metadata
|
|
58
58
|
const meta = JSON.parse(result.metadata);
|
|
59
59
|
console.log("Macros expanded:", meta);
|
|
60
|
-
|
|
60
|
+
}
|
|
61
61
|
``` ## When to Use
|
|
62
62
|
Use `transformSync` when:
|
|
63
63
|
- Building custom integrations
|
|
@@ -16,57 +16,57 @@
|
|
|
16
16
|
Built-in macros don't require imports. Just use them with `@derive`:
|
|
17
17
|
```
|
|
18
18
|
/** @derive(Debug, Clone, PartialEq) */
|
|
19
|
-
class User
|
|
19
|
+
class User {
|
|
20
20
|
name: string;
|
|
21
21
|
age: number;
|
|
22
22
|
|
|
23
|
-
constructor(name: string, age: number)
|
|
23
|
+
constructor(name: string, age: number) {
|
|
24
24
|
this.name = name;
|
|
25
25
|
this.age = age;
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
28
|
``` ## Interface Support
|
|
29
29
|
All built-in macros work with interfaces. For interfaces, methods are generated as functions in a namespace with the same name, using `self` as the first parameter:
|
|
30
30
|
```
|
|
31
31
|
/** @derive(Debug, Clone, PartialEq) */
|
|
32
|
-
interface Point
|
|
32
|
+
interface Point {
|
|
33
33
|
x: number;
|
|
34
34
|
y: number;
|
|
35
|
-
|
|
35
|
+
}
|
|
36
36
|
|
|
37
37
|
// Generated namespace:
|
|
38
|
-
// namespace Point
|
|
39
|
-
// export function toString(self: Point): string
|
|
40
|
-
// export function clone(self: Point): Point
|
|
41
|
-
// export function equals(self: Point, other: Point): boolean
|
|
42
|
-
// export function hashCode(self: Point): number
|
|
43
|
-
//
|
|
38
|
+
// namespace Point {
|
|
39
|
+
// export function toString(self: Point): string { ... }
|
|
40
|
+
// export function clone(self: Point): Point { ... }
|
|
41
|
+
// export function equals(self: Point, other: Point): boolean { ... }
|
|
42
|
+
// export function hashCode(self: Point): number { ... }
|
|
43
|
+
// }
|
|
44
44
|
|
|
45
|
-
const point: Point =
|
|
45
|
+
const point: Point = { x: 10, y: 20 };
|
|
46
46
|
|
|
47
47
|
// Use the namespace functions
|
|
48
|
-
console.log(Point.toString(point)); // "Point
|
|
49
|
-
const copy = Point.clone(point); //
|
|
48
|
+
console.log(Point.toString(point)); // "Point { x: 10, y: 20 }"
|
|
49
|
+
const copy = Point.clone(point); // { x: 10, y: 20 }
|
|
50
50
|
console.log(Point.equals(point, copy)); // true
|
|
51
51
|
``` ## Enum Support
|
|
52
52
|
All built-in macros work with enums. For enums, methods are generated as functions in a namespace with the same name:
|
|
53
53
|
```
|
|
54
54
|
/** @derive(Debug, Clone, PartialEq, Serialize, Deserialize) */
|
|
55
|
-
enum Status
|
|
55
|
+
enum Status {
|
|
56
56
|
Active = "active",
|
|
57
57
|
Inactive = "inactive",
|
|
58
58
|
Pending = "pending",
|
|
59
|
-
|
|
59
|
+
}
|
|
60
60
|
|
|
61
61
|
// Generated namespace:
|
|
62
|
-
// namespace Status
|
|
63
|
-
// export function toString(value: Status): string
|
|
64
|
-
// export function clone(value: Status): Status
|
|
65
|
-
// export function equals(a: Status, b: Status): boolean
|
|
66
|
-
// export function hashCode(value: Status): number
|
|
67
|
-
// export function toJSON(value: Status): string | number
|
|
68
|
-
// export function fromJSON(data: unknown): Status
|
|
69
|
-
//
|
|
62
|
+
// namespace Status {
|
|
63
|
+
// export function toString(value: Status): string { ... }
|
|
64
|
+
// export function clone(value: Status): Status { ... }
|
|
65
|
+
// export function equals(a: Status, b: Status): boolean { ... }
|
|
66
|
+
// export function hashCode(value: Status): number { ... }
|
|
67
|
+
// export function toJSON(value: Status): string | number { ... }
|
|
68
|
+
// export function fromJSON(data: unknown): Status { ... }
|
|
69
|
+
// }
|
|
70
70
|
|
|
71
71
|
// Use the namespace functions
|
|
72
72
|
console.log(Status.toString(Status.Active)); // "Status.Active"
|
|
@@ -77,24 +77,24 @@ const parsed = Status.fromJSON("active"); // Status.Active
|
|
|
77
77
|
All built-in macros work with type aliases. For object type aliases, field-aware methods are generated in a namespace:
|
|
78
78
|
```
|
|
79
79
|
/** @derive(Debug, Clone, PartialEq, Serialize, Deserialize) */
|
|
80
|
-
type Point =
|
|
80
|
+
type Point = {
|
|
81
81
|
x: number;
|
|
82
82
|
y: number;
|
|
83
|
-
|
|
83
|
+
};
|
|
84
84
|
|
|
85
85
|
// Generated namespace:
|
|
86
|
-
// namespace Point
|
|
87
|
-
// export function toString(value: Point): string
|
|
88
|
-
// export function clone(value: Point): Point
|
|
89
|
-
// export function equals(a: Point, b: Point): boolean
|
|
90
|
-
// export function hashCode(value: Point): number
|
|
91
|
-
// export function toJSON(value: Point): Record
|
|
92
|
-
// export function fromJSON(data: unknown): Point
|
|
93
|
-
//
|
|
86
|
+
// namespace Point {
|
|
87
|
+
// export function toString(value: Point): string { ... }
|
|
88
|
+
// export function clone(value: Point): Point { ... }
|
|
89
|
+
// export function equals(a: Point, b: Point): boolean { ... }
|
|
90
|
+
// export function hashCode(value: Point): number { ... }
|
|
91
|
+
// export function toJSON(value: Point): Record<string, unknown> { ... }
|
|
92
|
+
// export function fromJSON(data: unknown): Point { ... }
|
|
93
|
+
// }
|
|
94
94
|
|
|
95
|
-
const point: Point =
|
|
96
|
-
console.log(Point.toString(point)); // "Point
|
|
97
|
-
const copy = Point.clone(point); //
|
|
95
|
+
const point: Point = { x: 10, y: 20 };
|
|
96
|
+
console.log(Point.toString(point)); // "Point { x: 10, y: 20 }"
|
|
97
|
+
const copy = Point.clone(point); // { x: 10, y: 20 }
|
|
98
98
|
console.log(Point.equals(point, copy)); // true
|
|
99
99
|
``` Union type aliases also work, using JSON-based implementations:
|
|
100
100
|
```
|
|
@@ -102,7 +102,7 @@ console.log(Point.equals(point, copy)); // true
|
|
|
102
102
|
type ApiStatus = "loading" | "success" | "error";
|
|
103
103
|
|
|
104
104
|
const status: ApiStatus = "success";
|
|
105
|
-
console.log(ApiStatus.toString(status)); // "ApiStatus(
|
|
105
|
+
console.log(ApiStatus.toString(status)); // "ApiStatus(\\"success\\")"
|
|
106
106
|
console.log(ApiStatus.equals("success", "success")); // true
|
|
107
107
|
``` ## Combining Macros
|
|
108
108
|
All macros can be used together. They don't conflict and each generates independent methods:
|
|
@@ -111,7 +111,7 @@ const user = new User("Alice", 30);
|
|
|
111
111
|
|
|
112
112
|
// Debug
|
|
113
113
|
console.log(user.toString());
|
|
114
|
-
// "User
|
|
114
|
+
// "User { name: Alice, age: 30 }"
|
|
115
115
|
|
|
116
116
|
// Clone
|
|
117
117
|
const copy = user.clone();
|
|
@@ -37,8 +37,8 @@
|
|
|
37
37
|
For custom macro development, `macroforge_ts` re-exports everything you need:
|
|
38
38
|
```
|
|
39
39
|
// Convenient re-exports for macro development
|
|
40
|
-
use macroforge_ts::macros
|
|
41
|
-
use macroforge_ts::ts_syn
|
|
40
|
+
use macroforge_ts::macros::{ts_macro_derive, body, ts_template, above, below, signature};
|
|
41
|
+
use macroforge_ts::ts_syn::{Data, DeriveInput, MacroforgeError, TsStream, parse_ts_macro_input};
|
|
42
42
|
|
|
43
43
|
// Also available: raw crate access and SWC modules
|
|
44
44
|
use macroforge_ts::swc_core;
|
|
@@ -25,21 +25,21 @@ class User {
|
|
|
25
25
|
``` ### The import macro Statement
|
|
26
26
|
To use macros from external packages, you must declare them with `import macro`:
|
|
27
27
|
```
|
|
28
|
-
/** import macro
|
|
28
|
+
/** import macro { MacroName } from "package-name"; */
|
|
29
29
|
``` Syntax rules:
|
|
30
30
|
- Must be inside a JSDoc comment (`/** */`)
|
|
31
31
|
- Can appear anywhere in the file (typically at the top)
|
|
32
32
|
- Multiple macros can be imported: `import macro { A, B } from "pkg";`
|
|
33
33
|
- Multiple import statements can be used for different packages
|
|
34
34
|
```
|
|
35
|
-
/** import macro
|
|
36
|
-
/** import macro
|
|
35
|
+
/** import macro { JSON, Validate } from "@my/macros"; */
|
|
36
|
+
/** import macro { Builder } from "@other/macros"; */
|
|
37
37
|
|
|
38
38
|
/** @derive(JSON, Validate, Builder) */
|
|
39
|
-
class User
|
|
39
|
+
class User {
|
|
40
40
|
name: string;
|
|
41
41
|
email: string;
|
|
42
|
-
|
|
42
|
+
}
|
|
43
43
|
``` **Built-in macros Built-in macros (Debug, Clone, Default, Hash, Ord, PartialEq, PartialOrd, Serialize, Deserialize) do not require an import statement. ### Field Attributes
|
|
44
44
|
Macros can define field-level attributes to customize behavior per field:
|
|
45
45
|
****Before:**
|
|
@@ -8,52 +8,52 @@
|
|
|
8
8
|
4. Building and publishing as an npm package
|
|
9
9
|
## Quick Example
|
|
10
10
|
```
|
|
11
|
-
use macroforge_ts::macros
|
|
12
|
-
use macroforge_ts::ts_syn
|
|
11
|
+
use macroforge_ts::macros::{ts_macro_derive, body};
|
|
12
|
+
use macroforge_ts::ts_syn::{Data, DeriveInput, MacroforgeError, TsStream, parse_ts_macro_input};
|
|
13
13
|
|
|
14
14
|
#[ts_macro_derive(
|
|
15
15
|
JSON,
|
|
16
16
|
description = "Generates toJSON() returning a plain object"
|
|
17
17
|
)]
|
|
18
|
-
pub fn derive_json(mut input: TsStream) -> Result
|
|
18
|
+
pub fn derive_json(mut input: TsStream) -> Result<TsStream, MacroforgeError> {
|
|
19
19
|
let input = parse_ts_macro_input!(input as DeriveInput);
|
|
20
20
|
|
|
21
|
-
match
|
|
22
|
-
Data::Class(class) =>
|
|
23
|
-
Ok(body!
|
|
24
|
-
toJSON(): Record
|
|
25
|
-
return
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
21
|
+
match &input.data {
|
|
22
|
+
Data::Class(class) => {
|
|
23
|
+
Ok(body! {
|
|
24
|
+
toJSON(): Record<string, unknown> {
|
|
25
|
+
return {
|
|
26
|
+
{#for field in class.field_names()}
|
|
27
|
+
@{field}: this.@{field},
|
|
28
|
+
{/for}
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
})
|
|
32
|
+
}
|
|
33
33
|
_ => Err(MacroforgeError::new(
|
|
34
34
|
input.decorator_span(),
|
|
35
35
|
"@derive(JSON) only works on classes",
|
|
36
36
|
)),
|
|
37
|
-
|
|
38
|
-
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
39
|
``` ## Using Custom Macros
|
|
40
40
|
Once your macro package is published, users can import and use it:
|
|
41
41
|
```
|
|
42
|
-
/** import macro
|
|
42
|
+
/** import macro { JSON } from "@my/macros"; */
|
|
43
43
|
|
|
44
44
|
/** @derive(JSON) */
|
|
45
|
-
class User
|
|
45
|
+
class User {
|
|
46
46
|
name: string;
|
|
47
47
|
age: number;
|
|
48
48
|
|
|
49
|
-
constructor(name: string, age: number)
|
|
49
|
+
constructor(name: string, age: number) {
|
|
50
50
|
this.name = name;
|
|
51
51
|
this.age = age;
|
|
52
|
-
|
|
53
|
-
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
54
|
|
|
55
55
|
const user = new User("Alice", 30);
|
|
56
|
-
console.log(user.toJSON()); //
|
|
56
|
+
console.log(user.toJSON()); // { name: "Alice", age: 30 }
|
|
57
57
|
``` > **Note:** The import macro comment tells Macroforge which package provides the macro. ## Getting Started
|
|
58
58
|
Follow these guides to create your own macros:
|
|
59
59
|
- [Set up a Rust macro crate](../docs/custom-macros/rust-setup)
|
|
@@ -25,7 +25,7 @@ crate-type = ["cdylib"]
|
|
|
25
25
|
|
|
26
26
|
[dependencies]
|
|
27
27
|
macroforge_ts = "0.1"
|
|
28
|
-
napi =
|
|
28
|
+
napi = { version = "3", features = ["napi8", "compat-mode"] }
|
|
29
29
|
napi-derive = "3"
|
|
30
30
|
|
|
31
31
|
[build-dependencies]
|
|
@@ -36,67 +36,67 @@ lto = true
|
|
|
36
36
|
strip = true
|
|
37
37
|
``` ## Create build.rs
|
|
38
38
|
```
|
|
39
|
-
fn main()
|
|
39
|
+
fn main() {
|
|
40
40
|
napi_build::setup();
|
|
41
|
-
|
|
41
|
+
}
|
|
42
42
|
``` ## Create src/lib.rs
|
|
43
43
|
```
|
|
44
|
-
use macroforge_ts::macros
|
|
45
|
-
use macroforge_ts::ts_syn
|
|
44
|
+
use macroforge_ts::macros::{ts_macro_derive, body};
|
|
45
|
+
use macroforge_ts::ts_syn::{
|
|
46
46
|
Data, DeriveInput, MacroforgeError, TsStream, parse_ts_macro_input,
|
|
47
|
-
|
|
47
|
+
};
|
|
48
48
|
|
|
49
49
|
#[ts_macro_derive(
|
|
50
50
|
JSON,
|
|
51
51
|
description = "Generates toJSON() returning a plain object"
|
|
52
52
|
)]
|
|
53
|
-
pub fn derive_json(mut input: TsStream) -> Result
|
|
53
|
+
pub fn derive_json(mut input: TsStream) -> Result<TsStream, MacroforgeError> {
|
|
54
54
|
let input = parse_ts_macro_input!(input as DeriveInput);
|
|
55
55
|
|
|
56
|
-
match
|
|
57
|
-
Data::Class(class) =>
|
|
58
|
-
Ok(body!
|
|
59
|
-
toJSON(): Record
|
|
60
|
-
return
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
56
|
+
match &input.data {
|
|
57
|
+
Data::Class(class) => {
|
|
58
|
+
Ok(body! {
|
|
59
|
+
toJSON(): Record<string, unknown> {
|
|
60
|
+
return {
|
|
61
|
+
{#for field in class.field_names()}
|
|
62
|
+
@{field}: this.@{field},
|
|
63
|
+
{/for}
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
})
|
|
67
|
+
}
|
|
68
68
|
_ => Err(MacroforgeError::new(
|
|
69
69
|
input.decorator_span(),
|
|
70
70
|
"@derive(JSON) only works on classes",
|
|
71
71
|
)),
|
|
72
|
-
|
|
73
|
-
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
74
|
``` ## Create package.json
|
|
75
75
|
```
|
|
76
|
-
|
|
76
|
+
{
|
|
77
77
|
"name": "@my-org/macros",
|
|
78
78
|
"version": "0.1.0",
|
|
79
79
|
"main": "index.js",
|
|
80
80
|
"types": "index.d.ts",
|
|
81
|
-
"napi":
|
|
81
|
+
"napi": {
|
|
82
82
|
"name": "my-macros",
|
|
83
|
-
"triples":
|
|
83
|
+
"triples": {
|
|
84
84
|
"defaults": true
|
|
85
|
-
|
|
86
|
-
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
87
|
"files": [
|
|
88
88
|
"index.js",
|
|
89
89
|
"index.d.ts",
|
|
90
90
|
"*.node"
|
|
91
91
|
],
|
|
92
|
-
"scripts":
|
|
92
|
+
"scripts": {
|
|
93
93
|
"build": "napi build --release",
|
|
94
94
|
"prepublishOnly": "napi build --release"
|
|
95
|
-
|
|
96
|
-
"devDependencies":
|
|
95
|
+
},
|
|
96
|
+
"devDependencies": {
|
|
97
97
|
"@napi-rs/cli": "^3.0.0-alpha.0"
|
|
98
|
-
|
|
99
|
-
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
100
|
``` ## Build the Package
|
|
101
101
|
```
|
|
102
102
|
# Build the native addon
|