@macroforge/mcp-server 0.1.33 → 0.1.35
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +68 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +46 -1
- package/dist/index.js.map +1 -1
- package/dist/tools/docs-loader.d.ts +133 -5
- package/dist/tools/docs-loader.d.ts.map +1 -1
- package/dist/tools/docs-loader.js +131 -15
- package/dist/tools/docs-loader.js.map +1 -1
- package/dist/tools/index.d.ts +48 -1
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +163 -14
- package/dist/tools/index.js.map +1 -1
- package/docs/api/api-overview.md +24 -46
- package/docs/api/expand-sync.md +24 -51
- package/docs/api/native-plugin.md +24 -56
- package/docs/api/position-mapper.md +34 -76
- package/docs/api/transform-sync.md +27 -59
- package/docs/builtin-macros/clone.md +45 -104
- package/docs/builtin-macros/debug.md +33 -104
- package/docs/builtin-macros/default.md +78 -114
- package/docs/builtin-macros/deserialize.md +93 -273
- package/docs/builtin-macros/hash.md +58 -100
- package/docs/builtin-macros/macros-overview.md +42 -103
- package/docs/builtin-macros/ord.md +65 -133
- package/docs/builtin-macros/partial-eq.md +53 -179
- package/docs/builtin-macros/partial-ord.md +67 -159
- package/docs/builtin-macros/serialize.md +64 -194
- package/docs/concepts/architecture.md +40 -99
- package/docs/concepts/derive-system.md +129 -125
- package/docs/concepts/how-macros-work.md +52 -84
- package/docs/custom-macros/custom-overview.md +17 -39
- package/docs/custom-macros/rust-setup.md +22 -55
- package/docs/custom-macros/ts-macro-derive.md +43 -107
- package/docs/custom-macros/ts-quote.md +177 -507
- package/docs/getting-started/first-macro.md +108 -33
- package/docs/getting-started/installation.md +32 -73
- package/docs/integration/cli.md +70 -156
- package/docs/integration/configuration.md +32 -75
- package/docs/integration/integration-overview.md +16 -55
- package/docs/integration/mcp-server.md +30 -69
- package/docs/integration/svelte-preprocessor.md +60 -83
- package/docs/integration/typescript-plugin.md +32 -74
- package/docs/integration/vite-plugin.md +30 -79
- package/docs/language-servers/ls-overview.md +22 -46
- package/docs/language-servers/svelte.md +30 -69
- package/docs/language-servers/zed.md +34 -72
- package/docs/roadmap/roadmap.md +54 -130
- package/docs/sections.json +3 -262
- package/package.json +2 -2
|
@@ -1,123 +1,81 @@
|
|
|
1
1
|
# Hash
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The `Hash` macro generates a `hashCode()` method for computing numeric hash codes.
|
|
4
|
+
This is analogous to Rust's `Hash` trait and Java's `hashCode()` method, enabling
|
|
5
|
+
objects to be used as keys in hash-based collections.
|
|
4
6
|
|
|
5
|
-
##
|
|
7
|
+
## Generated Output
|
|
6
8
|
|
|
7
|
-
|
|
9
|
+
| Type | Generated Code | Description |
|
|
10
|
+
|------|----------------|-------------|
|
|
11
|
+
| Class | `hashCode(): number` | Instance method computing hash from all fields |
|
|
12
|
+
| Enum | `hashCodeEnumName(value: EnumName): number` | Standalone function hashing by enum value |
|
|
13
|
+
| Interface | `hashCodeInterfaceName(value: InterfaceName): number` | Standalone function computing hash |
|
|
14
|
+
| Type Alias | `hashCodeTypeName(value: TypeName): number` | Standalone function computing hash |
|
|
8
15
|
|
|
9
|
-
|
|
10
|
-
const p1 = new Point(10, 20);
|
|
11
|
-
const p2 = new Point(10, 20);
|
|
12
|
-
const p3 = new Point(5, 5);
|
|
16
|
+
## Configuration
|
|
13
17
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
+
The `functionNamingStyle` option in `macroforge.json` controls naming:
|
|
19
|
+
- `"suffix"` (default): Suffixes with type name (e.g., `hashCodeMyType`)
|
|
20
|
+
- `"prefix"`: Prefixes with type name (e.g., `myTypeHashCode`)
|
|
21
|
+
- `"generic"`: Uses TypeScript generics (e.g., `hashCode<T extends MyType>`)
|
|
22
|
+
- `"namespace"`: Legacy namespace wrapping
|
|
18
23
|
|
|
19
24
|
## Hash Algorithm
|
|
20
25
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
- `number` → Integers use bitwise OR (`| 0`), floats are stringified and hashed
|
|
24
|
-
|
|
25
|
-
- `string` → Character-by-character hash: `(h * 31 + charCode) | 0`
|
|
26
|
-
|
|
27
|
-
- `boolean` → `1231` for true, `1237` for false (Java convention)
|
|
28
|
-
|
|
29
|
-
- `bigint` → Converted to string and hashed character-by-character
|
|
30
|
-
|
|
31
|
-
- `Date` → Uses `getTime() | 0` for timestamp hash
|
|
32
|
-
|
|
33
|
-
- `Array` → Combines element hashes with `h * 31 + elementHash`
|
|
34
|
-
|
|
35
|
-
- `Map/Set` → Combines all entry hashes
|
|
36
|
-
|
|
37
|
-
- `Object` → Calls `hashCode()` if available, otherwise JSON stringifies and hashes
|
|
38
|
-
|
|
39
|
-
- `null` → Returns 0
|
|
40
|
-
|
|
41
|
-
- `undefined` → Returns 1
|
|
42
|
-
|
|
43
|
-
## Field Options
|
|
44
|
-
|
|
45
|
-
### @hash(skip)
|
|
46
|
-
|
|
47
|
-
Use `@hash(skip)` to exclude a field from hash computation:
|
|
48
|
-
|
|
49
|
-
<MacroExample before={data.examples.skip.before} after={data.examples.skip.after} />
|
|
50
|
-
|
|
51
|
-
```typescript
|
|
52
|
-
const user1 = new User(1, "Alice", new Date("2024-01-01"));
|
|
53
|
-
const user2 = new User(1, "Alice", new Date("2024-12-01"));
|
|
54
|
-
|
|
55
|
-
console.log(user1.hashCode() === user2.hashCode()); // true (lastLogin is skipped)
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
## Use with PartialEq
|
|
59
|
-
|
|
60
|
-
Hash is often used together with PartialEq. Objects that are equal should have the same hash code:
|
|
61
|
-
|
|
62
|
-
<InteractiveMacro code={`/** @derive(Hash, PartialEq) */
|
|
63
|
-
class Product {
|
|
64
|
-
sku: string;
|
|
65
|
-
name: string;
|
|
66
|
-
|
|
67
|
-
constructor(sku: string, name: string) {
|
|
68
|
-
this.sku = sku;
|
|
69
|
-
this.name = name;
|
|
70
|
-
}
|
|
71
|
-
}`} />
|
|
72
|
-
|
|
73
|
-
```typescript
|
|
74
|
-
const p1 = new Product("ABC123", "Widget");
|
|
75
|
-
const p2 = new Product("ABC123", "Widget");
|
|
76
|
-
|
|
77
|
-
// Equal objects have equal hash codes
|
|
78
|
-
console.log(p1.equals(p2)); // true
|
|
79
|
-
console.log(p1.hashCode() === p2.hashCode()); // true
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
## Interface Support
|
|
83
|
-
|
|
84
|
-
Hash also works with interfaces. For interfaces, a namespace is generated with a `hashCode` function:
|
|
26
|
+
Uses the standard polynomial rolling hash algorithm:
|
|
85
27
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
console.log(Point.hashCode(p)); // numeric hash value
|
|
28
|
+
```text
|
|
29
|
+
hash = 17 // Initial seed
|
|
30
|
+
for each field:
|
|
31
|
+
hash = (hash * 31 + fieldHash) | 0 // Bitwise OR keeps it 32-bit integer
|
|
91
32
|
```
|
|
92
33
|
|
|
93
|
-
|
|
34
|
+
This algorithm is consistent with Java's `Objects.hash()` implementation.
|
|
94
35
|
|
|
95
|
-
|
|
36
|
+
## Type-Specific Hashing
|
|
96
37
|
|
|
97
|
-
|
|
38
|
+
| Type | Hash Strategy |
|
|
39
|
+
|------|---------------|
|
|
40
|
+
| `number` | Integer: direct value; Float: string hash of decimal |
|
|
41
|
+
| `bigint` | String hash of decimal representation |
|
|
42
|
+
| `string` | Character-by-character polynomial hash |
|
|
43
|
+
| `boolean` | 1231 for true, 1237 for false (Java convention) |
|
|
44
|
+
| `Date` | `getTime()` timestamp |
|
|
45
|
+
| Arrays | Element-by-element hash combination |
|
|
46
|
+
| `Map` | Entry-by-entry key+value hash |
|
|
47
|
+
| `Set` | Element-by-element hash |
|
|
48
|
+
| Objects | Calls `hashCode()` if available, else JSON string hash |
|
|
98
49
|
|
|
99
|
-
|
|
100
|
-
console.log(Status.hashCode(Status.Active)); // consistent hash
|
|
101
|
-
console.log(Status.hashCode(Status.Inactive)); // different hash
|
|
102
|
-
```
|
|
50
|
+
## Field-Level Options
|
|
103
51
|
|
|
104
|
-
|
|
52
|
+
The `@hash` decorator supports:
|
|
105
53
|
|
|
106
|
-
|
|
54
|
+
- `skip` - Exclude the field from hash calculation
|
|
107
55
|
|
|
108
|
-
|
|
56
|
+
## Example
|
|
109
57
|
|
|
110
58
|
```typescript
|
|
111
|
-
|
|
112
|
-
|
|
59
|
+
@derive(Hash, PartialEq)
|
|
60
|
+
class User {
|
|
61
|
+
id: number;
|
|
62
|
+
name: string;
|
|
63
|
+
|
|
64
|
+
@hash(skip) // Cached value shouldn't affect hash
|
|
65
|
+
cachedScore: number;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Generated:
|
|
69
|
+
// hashCode(): number {
|
|
70
|
+
// let hash = 17;
|
|
71
|
+
// hash = (hash * 31 + (Number.isInteger(this.id) ? this.id | 0 : ...)) | 0;
|
|
72
|
+
// hash = (hash * 31 + (this.name ?? '').split('').reduce(...)) | 0;
|
|
73
|
+
// return hash;
|
|
74
|
+
// }
|
|
113
75
|
```
|
|
114
76
|
|
|
115
|
-
|
|
77
|
+
## Hash Contract
|
|
116
78
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
```typescript
|
|
121
|
-
console.log(Result.hashCode("success")); // hash of "success" string
|
|
122
|
-
console.log(Result.hashCode("error")); // hash of "error" string
|
|
123
|
-
```
|
|
79
|
+
Objects that are equal (`PartialEq`) should produce the same hash code.
|
|
80
|
+
When using `@hash(skip)`, ensure the same fields are skipped in both
|
|
81
|
+
`Hash` and `PartialEq` to maintain this contract.
|
|
@@ -1,50 +1,20 @@
|
|
|
1
1
|
# Built-in Macros
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
| [`
|
|
8
|
-
| `
|
|
9
|
-
|
|
|
10
|
-
|
|
11
|
-
| [`
|
|
12
|
-
| `
|
|
13
|
-
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
| [`Hash`]({base}/docs/builtin-macros/hash)
|
|
20
|
-
| `hashCode(): number`
|
|
21
|
-
| Generates a hash code for the object
|
|
22
|
-
|
|
23
|
-
| [`PartialEq`]({base}/docs/builtin-macros/partial-eq)
|
|
24
|
-
| `equals(other: T): boolean`
|
|
25
|
-
| Value equality comparison
|
|
26
|
-
|
|
27
|
-
| [`Ord`]({base}/docs/builtin-macros/ord)
|
|
28
|
-
| `compare(other: T): number`
|
|
29
|
-
| Total ordering comparison (-1, 0, 1)
|
|
30
|
-
|
|
31
|
-
| [`PartialOrd`]({base}/docs/builtin-macros/partial-ord)
|
|
32
|
-
| `partialCompare(other: T): number | null`
|
|
33
|
-
| Partial ordering comparison
|
|
34
|
-
|
|
35
|
-
| [`Serialize`]({base}/docs/builtin-macros/serialize)
|
|
36
|
-
| `toJSON(): Record<string, unknown>`
|
|
37
|
-
| JSON serialization with type handling
|
|
38
|
-
|
|
39
|
-
| [`Deserialize`]({base}/docs/builtin-macros/deserialize)
|
|
40
|
-
| `static fromJSON(data: unknown): T`
|
|
41
|
-
| JSON deserialization with validation
|
|
42
|
-
|
|
43
|
-
## Using Built-in Macros
|
|
44
|
-
|
|
45
|
-
Built-in macros don't require imports. Just use them with `@derive`:
|
|
46
|
-
|
|
47
|
-
```typescript
|
|
2
|
+
*Macroforge comes with built-in derive macros that cover the most common code generation needs. All macros work with classes, interfaces, enums, and type aliases.*
|
|
3
|
+
## Overview
|
|
4
|
+
| Macro | Generates | Description |
|
|
5
|
+
| --- | --- | --- |
|
|
6
|
+
| [`Debug`](../docs/builtin-macros/debug) | `toString(): string` | Human-readable string representation |
|
|
7
|
+
| [`Clone`](../docs/builtin-macros/clone) | `clone(): T` | Creates a deep copy of the object |
|
|
8
|
+
| [`Default`](../docs/builtin-macros/default) | `static default(): T` | Creates an instance with default values |
|
|
9
|
+
| [`Hash`](../docs/builtin-macros/hash) | `hashCode(): number` | Generates a hash code for the object |
|
|
10
|
+
| [`PartialEq`](../docs/builtin-macros/partial-eq) | `equals(other: T): boolean` | Value equality comparison |
|
|
11
|
+
| [`Ord`](../docs/builtin-macros/ord) | `compare(other: T): number` | Total ordering comparison (-1, 0, 1) |
|
|
12
|
+
| [`PartialOrd`](../docs/builtin-macros/partial-ord) | `partialCompare(other: T): number | null` | Partial ordering comparison |
|
|
13
|
+
| [`Serialize`](../docs/builtin-macros/serialize) | `toJSON(): Record<string, unknown>` | JSON serialization with type handling |
|
|
14
|
+
| [`Deserialize`](../docs/builtin-macros/deserialize) | `static fromJSON(data: unknown): T` | JSON deserialization with validation |
|
|
15
|
+
## Using Built-in Macros
|
|
16
|
+
Built-in macros don't require imports. Just use them with `@derive`:
|
|
17
|
+
```
|
|
48
18
|
/** @derive(Debug, Clone, PartialEq) */
|
|
49
19
|
class User {
|
|
50
20
|
name: string;
|
|
@@ -55,13 +25,9 @@ class User {
|
|
|
55
25
|
this.age = age;
|
|
56
26
|
}
|
|
57
27
|
}
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
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:
|
|
63
|
-
|
|
64
|
-
```typescript
|
|
28
|
+
``` ## Interface Support
|
|
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
|
+
```
|
|
65
31
|
/** @derive(Debug, Clone, PartialEq) */
|
|
66
32
|
interface Point {
|
|
67
33
|
x: number;
|
|
@@ -82,13 +48,9 @@ const point: Point = { x: 10, y: 20 };
|
|
|
82
48
|
console.log(Point.toString(point)); // "Point { x: 10, y: 20 }"
|
|
83
49
|
const copy = Point.clone(point); // { x: 10, y: 20 }
|
|
84
50
|
console.log(Point.equals(point, copy)); // true
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
All built-in macros work with enums. For enums, methods are generated as functions in a namespace with the same name:
|
|
90
|
-
|
|
91
|
-
```typescript
|
|
51
|
+
``` ## Enum Support
|
|
52
|
+
All built-in macros work with enums. For enums, methods are generated as functions in a namespace with the same name:
|
|
53
|
+
```
|
|
92
54
|
/** @derive(Debug, Clone, PartialEq, Serialize, Deserialize) */
|
|
93
55
|
enum Status {
|
|
94
56
|
Active = "active",
|
|
@@ -111,13 +73,9 @@ console.log(Status.toString(Status.Active)); // "Status.Active"
|
|
|
111
73
|
console.log(Status.equals(Status.Active, Status.Active)); // true
|
|
112
74
|
const json = Status.toJSON(Status.Pending); // "pending"
|
|
113
75
|
const parsed = Status.fromJSON("active"); // Status.Active
|
|
114
|
-
```
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
All built-in macros work with type aliases. For object type aliases, field-aware methods are generated in a namespace:
|
|
119
|
-
|
|
120
|
-
```typescript
|
|
76
|
+
``` ## Type Alias Support
|
|
77
|
+
All built-in macros work with type aliases. For object type aliases, field-aware methods are generated in a namespace:
|
|
78
|
+
```
|
|
121
79
|
/** @derive(Debug, Clone, PartialEq, Serialize, Deserialize) */
|
|
122
80
|
type Point = {
|
|
123
81
|
x: number;
|
|
@@ -138,24 +96,17 @@ const point: Point = { x: 10, y: 20 };
|
|
|
138
96
|
console.log(Point.toString(point)); // "Point { x: 10, y: 20 }"
|
|
139
97
|
const copy = Point.clone(point); // { x: 10, y: 20 }
|
|
140
98
|
console.log(Point.equals(point, copy)); // true
|
|
141
|
-
```
|
|
142
|
-
|
|
143
|
-
Union type aliases also work, using JSON-based implementations:
|
|
144
|
-
|
|
145
|
-
```typescript
|
|
99
|
+
``` Union type aliases also work, using JSON-based implementations:
|
|
100
|
+
```
|
|
146
101
|
/** @derive(Debug, PartialEq) */
|
|
147
102
|
type ApiStatus = "loading" | "success" | "error";
|
|
148
103
|
|
|
149
104
|
const status: ApiStatus = "success";
|
|
150
|
-
console.log(ApiStatus.toString(status)); // "ApiStatus(
|
|
105
|
+
console.log(ApiStatus.toString(status)); // "ApiStatus(\"success\")"
|
|
151
106
|
console.log(ApiStatus.equals("success", "success")); // true
|
|
152
|
-
```
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
All macros can be used together. They don't conflict and each generates independent methods:
|
|
157
|
-
|
|
158
|
-
```typescript
|
|
107
|
+
``` ## Combining Macros
|
|
108
|
+
All macros can be used together. They don't conflict and each generates independent methods:
|
|
109
|
+
```
|
|
159
110
|
const user = new User("Alice", 30);
|
|
160
111
|
|
|
161
112
|
// Debug
|
|
@@ -168,26 +119,14 @@ console.log(copy.name); // "Alice"
|
|
|
168
119
|
|
|
169
120
|
// Eq
|
|
170
121
|
console.log(user.equals(copy)); // true
|
|
171
|
-
```
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
- [**
|
|
178
|
-
|
|
179
|
-
- [**
|
|
180
|
-
|
|
181
|
-
- [**
|
|
182
|
-
|
|
183
|
-
- [**Hash**]({base}/docs/builtin-macros/hash) - Hash code generation for use in maps and sets
|
|
184
|
-
|
|
185
|
-
- [**PartialEq**]({base}/docs/builtin-macros/partial-eq) - Value-based equality comparison
|
|
186
|
-
|
|
187
|
-
- [**Ord**]({base}/docs/builtin-macros/ord) - Total ordering for sorting
|
|
188
|
-
|
|
189
|
-
- [**PartialOrd**]({base}/docs/builtin-macros/partial-ord) - Partial ordering comparison
|
|
190
|
-
|
|
191
|
-
- [**Serialize**]({base}/docs/builtin-macros/serialize) - JSON serialization with serde-style options
|
|
192
|
-
|
|
193
|
-
- [**Deserialize**]({base}/docs/builtin-macros/deserialize) - JSON deserialization with validation
|
|
122
|
+
``` ## Detailed Documentation
|
|
123
|
+
Each macro has its own options and behaviors:
|
|
124
|
+
- [**Debug**](../docs/builtin-macros/debug) - Customizable field renaming and skipping
|
|
125
|
+
- [**Clone**](../docs/builtin-macros/clone) - Deep copying for all field types
|
|
126
|
+
- [**Default**](../docs/builtin-macros/default) - Default value generation with field attributes
|
|
127
|
+
- [**Hash**](../docs/builtin-macros/hash) - Hash code generation for use in maps and sets
|
|
128
|
+
- [**PartialEq**](../docs/builtin-macros/partial-eq) - Value-based equality comparison
|
|
129
|
+
- [**Ord**](../docs/builtin-macros/ord) - Total ordering for sorting
|
|
130
|
+
- [**PartialOrd**](../docs/builtin-macros/partial-ord) - Partial ordering comparison
|
|
131
|
+
- [**Serialize**](../docs/builtin-macros/serialize) - JSON serialization with serde-style options
|
|
132
|
+
- [**Deserialize**](../docs/builtin-macros/deserialize) - JSON deserialization with validation
|
|
@@ -1,159 +1,91 @@
|
|
|
1
1
|
# Ord
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The `Ord` macro generates a `compareTo()` method for **total ordering** comparison.
|
|
4
|
+
This is analogous to Rust's `Ord` trait, enabling objects to be sorted and
|
|
5
|
+
compared with a guaranteed ordering relationship.
|
|
4
6
|
|
|
5
|
-
##
|
|
7
|
+
## Generated Output
|
|
6
8
|
|
|
7
|
-
|
|
9
|
+
| Type | Generated Code | Description |
|
|
10
|
+
|------|----------------|-------------|
|
|
11
|
+
| Class | `compareTo(other): number` | Instance method returning -1, 0, or 1 |
|
|
12
|
+
| Enum | `compareEnumName(a: EnumName, b: EnumName): number` | Standalone function comparing enum values |
|
|
13
|
+
| Interface | `compareInterfaceName(a: InterfaceName, b: InterfaceName): number` | Standalone function comparing fields |
|
|
14
|
+
| Type Alias | `compareTypeName(a: TypeName, b: TypeName): number` | Standalone function with type-appropriate comparison |
|
|
8
15
|
|
|
9
|
-
|
|
10
|
-
const v1 = new Version(1, 0, 0);
|
|
11
|
-
const v2 = new Version(1, 2, 0);
|
|
12
|
-
const v3 = new Version(1, 2, 0);
|
|
13
|
-
|
|
14
|
-
console.log(v1.compareTo(v2)); // -1 (v1 < v2)
|
|
15
|
-
console.log(v2.compareTo(v1)); // 1 (v2 > v1)
|
|
16
|
-
console.log(v2.compareTo(v3)); // 0 (v2 == v3)
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
## Comparison Logic
|
|
20
|
-
|
|
21
|
-
The Ord macro compares fields in declaration order (lexicographic ordering). For each type:
|
|
22
|
-
|
|
23
|
-
- `number` / `bigint` → Direct numeric comparison
|
|
24
|
-
|
|
25
|
-
- `string` → Uses `localeCompare()` clamped to -1/0/1
|
|
26
|
-
|
|
27
|
-
- `boolean` → `false < true`
|
|
28
|
-
|
|
29
|
-
- `Date` → Compares timestamps via `getTime()`
|
|
30
|
-
|
|
31
|
-
- `Array` → Lexicographic: compares element-by-element, then length
|
|
32
|
-
|
|
33
|
-
- `Map/Set` → Size and content comparison
|
|
16
|
+
## Configuration
|
|
34
17
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
- `
|
|
18
|
+
The `functionNamingStyle` option in `macroforge.json` controls naming:
|
|
19
|
+
- `"suffix"` (default): Suffixes with type name (e.g., `compareMyType`)
|
|
20
|
+
- `"prefix"`: Prefixes with type name (e.g., `myTypeCompare`)
|
|
21
|
+
- `"generic"`: Uses TypeScript generics (e.g., `compare<T extends MyType>`)
|
|
22
|
+
- `"namespace"`: Legacy namespace wrapping
|
|
38
23
|
|
|
39
24
|
## Return Values
|
|
40
25
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
- `-1` → `this` is less than `other`
|
|
44
|
-
|
|
45
|
-
- `0` → `this` equals `other`
|
|
26
|
+
Unlike `PartialOrd`, `Ord` provides **total ordering** - every pair of values
|
|
27
|
+
can be compared:
|
|
46
28
|
|
|
47
|
-
-
|
|
29
|
+
- **-1**: `this` is less than `other`
|
|
30
|
+
- **0**: `this` is equal to `other`
|
|
31
|
+
- **1**: `this` is greater than `other`
|
|
48
32
|
|
|
49
|
-
|
|
33
|
+
The method **never returns null** - all values must be comparable.
|
|
50
34
|
|
|
51
|
-
##
|
|
35
|
+
## Comparison Strategy
|
|
52
36
|
|
|
53
|
-
|
|
37
|
+
Fields are compared **lexicographically** in declaration order:
|
|
54
38
|
|
|
55
|
-
|
|
39
|
+
1. Compare first field
|
|
40
|
+
2. If not equal, return that result
|
|
41
|
+
3. Otherwise, compare next field
|
|
42
|
+
4. Continue until a difference is found or all fields are equal
|
|
56
43
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
```typescript
|
|
60
|
-
const t1 = new Task(1, "Bug fix", new Date("2024-01-01"));
|
|
61
|
-
const t2 = new Task(1, "Bug fix", new Date("2024-12-01"));
|
|
44
|
+
## Type-Specific Comparisons
|
|
62
45
|
|
|
63
|
-
|
|
64
|
-
|
|
46
|
+
| Type | Comparison Method |
|
|
47
|
+
|------|-------------------|
|
|
48
|
+
| `number`/`bigint` | Direct `<` and `>` comparison |
|
|
49
|
+
| `string` | `localeCompare()` (clamped to -1, 0, 1) |
|
|
50
|
+
| `boolean` | false < true |
|
|
51
|
+
| Arrays | Lexicographic element-by-element |
|
|
52
|
+
| `Date` | `getTime()` timestamp comparison |
|
|
53
|
+
| Objects | Calls `compareTo()` if available, else 0 |
|
|
65
54
|
|
|
66
|
-
##
|
|
55
|
+
## Field-Level Options
|
|
67
56
|
|
|
68
|
-
The
|
|
57
|
+
The `@ord` decorator supports:
|
|
69
58
|
|
|
70
|
-
|
|
71
|
-
class Score {
|
|
72
|
-
points: number;
|
|
73
|
-
name: string;
|
|
59
|
+
- `skip` - Exclude the field from ordering comparison
|
|
74
60
|
|
|
75
|
-
|
|
76
|
-
this.points = points;
|
|
77
|
-
this.name = name;
|
|
78
|
-
}
|
|
79
|
-
}`} />
|
|
61
|
+
## Example
|
|
80
62
|
|
|
81
63
|
```typescript
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
//
|
|
90
|
-
|
|
91
|
-
//
|
|
92
|
-
|
|
93
|
-
//
|
|
94
|
-
|
|
95
|
-
//
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
```typescript
|
|
105
|
-
const points: Point[] = [
|
|
106
|
-
{ x: 5, y: 10 },
|
|
107
|
-
{ x: 1, y: 20 },
|
|
108
|
-
{ x: 5, y: 5 }
|
|
109
|
-
];
|
|
110
|
-
|
|
111
|
-
points.sort((a, b) => Point.compareTo(a, b));
|
|
112
|
-
// Result: [{ x: 1, y: 20 }, { x: 5, y: 5 }, { x: 5, y: 10 }]
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
## Enum Support
|
|
116
|
-
|
|
117
|
-
Ord works with enums. For numeric enums, it compares the numeric values; for string enums, it uses string comparison:
|
|
118
|
-
|
|
119
|
-
<MacroExample before={data.examples.enum.before} after={data.examples.enum.after} />
|
|
120
|
-
|
|
121
|
-
```typescript
|
|
122
|
-
console.log(Priority.compareTo(Priority.Low, Priority.High)); // -1
|
|
123
|
-
console.log(Priority.compareTo(Priority.Critical, Priority.Low)); // 1
|
|
124
|
-
console.log(Priority.compareTo(Priority.Medium, Priority.Medium)); // 0
|
|
125
|
-
```
|
|
126
|
-
|
|
127
|
-
## Type Alias Support
|
|
128
|
-
|
|
129
|
-
Ord works with type aliases. For object types, it uses lexicographic field comparison:
|
|
130
|
-
|
|
131
|
-
<MacroExample before={data.examples.typeAlias.before} after={data.examples.typeAlias.after} />
|
|
132
|
-
|
|
133
|
-
```typescript
|
|
134
|
-
const c1: Coordinate = { x: 10, y: 20 };
|
|
135
|
-
const c2: Coordinate = { x: 10, y: 30 };
|
|
136
|
-
|
|
137
|
-
console.log(Coordinate.compareTo(c1, c2)); // -1 (c1 < c2)
|
|
64
|
+
@derive(Ord)
|
|
65
|
+
class Version {
|
|
66
|
+
major: number;
|
|
67
|
+
minor: number;
|
|
68
|
+
patch: number;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Generated:
|
|
72
|
+
// compareTo(other: Version): number {
|
|
73
|
+
// if (this === other) return 0;
|
|
74
|
+
// const typedOther = other;
|
|
75
|
+
// const cmp0 = this.major < typedOther.major ? -1 : this.major > typedOther.major ? 1 : 0;
|
|
76
|
+
// if (cmp0 !== 0) return cmp0;
|
|
77
|
+
// const cmp1 = this.minor < typedOther.minor ? -1 : ...;
|
|
78
|
+
// if (cmp1 !== 0) return cmp1;
|
|
79
|
+
// const cmp2 = this.patch < typedOther.patch ? -1 : ...;
|
|
80
|
+
// if (cmp2 !== 0) return cmp2;
|
|
81
|
+
// return 0;
|
|
82
|
+
// }
|
|
83
|
+
|
|
84
|
+
// Usage:
|
|
85
|
+
versions.sort((a, b) => a.compareTo(b));
|
|
138
86
|
```
|
|
139
87
|
|
|
140
88
|
## Ord vs PartialOrd
|
|
141
89
|
|
|
142
|
-
Use
|
|
143
|
-
|
|
144
|
-
<InteractiveMacro code={`// Ord: Total ordering - never returns null
|
|
145
|
-
/** @derive(Ord) */
|
|
146
|
-
class Version {
|
|
147
|
-
major: number;
|
|
148
|
-
minor: number;
|
|
149
|
-
constructor(major: number, minor: number) {
|
|
150
|
-
this.major = major;
|
|
151
|
-
this.minor = minor;
|
|
152
|
-
}
|
|
153
|
-
}`} />
|
|
154
|
-
|
|
155
|
-
```typescript
|
|
156
|
-
const v1 = new Version(1, 0);
|
|
157
|
-
const v2 = new Version(2, 0);
|
|
158
|
-
console.log(v1.compareTo(v2)); // Always -1, 0, or 1
|
|
159
|
-
```
|
|
90
|
+
- Use **Ord** when all values are comparable (total ordering)
|
|
91
|
+
- Use **PartialOrd** when some values may be incomparable (returns `Option<number>`)
|