@typra/emitter 0.2.0
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/dist/src/cleanup/generated-file.d.ts +6 -0
- package/dist/src/cleanup/generated-file.js +61 -0
- package/dist/src/cli.d.ts +2 -0
- package/dist/src/cli.js +110 -0
- package/dist/src/decorators.d.ts +56 -0
- package/dist/src/decorators.js +177 -0
- package/dist/src/emitter.d.ts +13 -0
- package/dist/src/emitter.js +137 -0
- package/dist/src/generate.d.ts +86 -0
- package/dist/src/generate.js +104 -0
- package/dist/src/index.d.ts +4 -0
- package/dist/src/index.js +5 -0
- package/dist/src/ir/ast.d.ts +235 -0
- package/dist/src/ir/ast.js +589 -0
- package/dist/src/ir/declarations.d.ts +364 -0
- package/dist/src/ir/declarations.js +23 -0
- package/dist/src/ir/expansion.d.ts +140 -0
- package/dist/src/ir/expansion.js +407 -0
- package/dist/src/ir/lower.d.ts +53 -0
- package/dist/src/ir/lower.js +480 -0
- package/dist/src/ir/utilities.d.ts +12 -0
- package/dist/src/ir/utilities.js +39 -0
- package/dist/src/ir/visitor.d.ts +29 -0
- package/dist/src/ir/visitor.js +48 -0
- package/dist/src/languages/csharp/driver.d.ts +5 -0
- package/dist/src/languages/csharp/driver.js +315 -0
- package/dist/src/languages/csharp/emitter.d.ts +33 -0
- package/dist/src/languages/csharp/emitter.js +1140 -0
- package/dist/src/languages/csharp/scaffolding.d.ts +18 -0
- package/dist/src/languages/csharp/scaffolding.js +591 -0
- package/dist/src/languages/csharp/test-emitter.d.ts +43 -0
- package/dist/src/languages/csharp/test-emitter.js +274 -0
- package/dist/src/languages/csharp/visitor.d.ts +14 -0
- package/dist/src/languages/csharp/visitor.js +79 -0
- package/dist/src/languages/go/driver.d.ts +12 -0
- package/dist/src/languages/go/driver.js +128 -0
- package/dist/src/languages/go/emitter.d.ts +33 -0
- package/dist/src/languages/go/emitter.js +879 -0
- package/dist/src/languages/go/scaffolding.d.ts +18 -0
- package/dist/src/languages/go/scaffolding.js +53 -0
- package/dist/src/languages/go/test-emitter.d.ts +20 -0
- package/dist/src/languages/go/test-emitter.js +300 -0
- package/dist/src/languages/go/visitor.d.ts +14 -0
- package/dist/src/languages/go/visitor.js +78 -0
- package/dist/src/languages/markdown/driver.d.ts +19 -0
- package/dist/src/languages/markdown/driver.js +408 -0
- package/dist/src/languages/python/driver.d.ts +14 -0
- package/dist/src/languages/python/driver.js +372 -0
- package/dist/src/languages/python/emitter.d.ts +31 -0
- package/dist/src/languages/python/emitter.js +856 -0
- package/dist/src/languages/python/scaffolding.d.ts +33 -0
- package/dist/src/languages/python/scaffolding.js +279 -0
- package/dist/src/languages/python/test-emitter.d.ts +29 -0
- package/dist/src/languages/python/test-emitter.js +388 -0
- package/dist/src/languages/python/visitor.d.ts +14 -0
- package/dist/src/languages/python/visitor.js +65 -0
- package/dist/src/languages/rust/driver.d.ts +13 -0
- package/dist/src/languages/rust/driver.js +624 -0
- package/dist/src/languages/rust/emitter.d.ts +45 -0
- package/dist/src/languages/rust/emitter.js +1596 -0
- package/dist/src/languages/rust/visitor.d.ts +25 -0
- package/dist/src/languages/rust/visitor.js +153 -0
- package/dist/src/languages/typescript/driver.d.ts +8 -0
- package/dist/src/languages/typescript/driver.js +209 -0
- package/dist/src/languages/typescript/emitter.d.ts +42 -0
- package/dist/src/languages/typescript/emitter.js +904 -0
- package/dist/src/languages/typescript/scaffolding.d.ts +32 -0
- package/dist/src/languages/typescript/scaffolding.js +303 -0
- package/dist/src/languages/typescript/test-emitter.d.ts +23 -0
- package/dist/src/languages/typescript/test-emitter.js +204 -0
- package/dist/src/languages/typescript/visitor.d.ts +14 -0
- package/dist/src/languages/typescript/visitor.js +64 -0
- package/dist/src/lib.d.ts +33 -0
- package/dist/src/lib.js +101 -0
- package/dist/src/testing/index.d.ts +2 -0
- package/dist/src/testing/index.js +8 -0
- package/dist/src/testing/test-context.d.ts +63 -0
- package/dist/src/testing/test-context.js +355 -0
- package/fixtures/shapes/main.tsp +43 -0
- package/fixtures/tspconfig.yaml +13 -0
- package/package.json +76 -0
- package/src/lib/main.tsp +110 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* C# scaffolding emitter — static support files.
|
|
3
|
+
*
|
|
4
|
+
* Replaces `context.cs.njk` and `utils.cs.njk` Nunjucks templates with typed
|
|
5
|
+
* TypeScript functions that produce the same C# source code.
|
|
6
|
+
*
|
|
7
|
+
* Emitted files:
|
|
8
|
+
* - Context.cs — LoadContext / SaveContext helper classes
|
|
9
|
+
* - Utils.cs — JsonUtils, YamlUtils, and internal Utils extension methods
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Emit the C# LoadContext / SaveContext file.
|
|
13
|
+
*/
|
|
14
|
+
export declare function emitCSharpContext(namespace: string): string;
|
|
15
|
+
/**
|
|
16
|
+
* Emit the C# utility classes file (JsonUtils, YamlUtils, Utils).
|
|
17
|
+
*/
|
|
18
|
+
export declare function emitCSharpUtils(namespace: string): string;
|
|
@@ -0,0 +1,591 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* C# scaffolding emitter — static support files.
|
|
3
|
+
*
|
|
4
|
+
* Replaces `context.cs.njk` and `utils.cs.njk` Nunjucks templates with typed
|
|
5
|
+
* TypeScript functions that produce the same C# source code.
|
|
6
|
+
*
|
|
7
|
+
* Emitted files:
|
|
8
|
+
* - Context.cs — LoadContext / SaveContext helper classes
|
|
9
|
+
* - Utils.cs — JsonUtils, YamlUtils, and internal Utils extension methods
|
|
10
|
+
*/
|
|
11
|
+
// ============================================================================
|
|
12
|
+
// Context.cs
|
|
13
|
+
// ============================================================================
|
|
14
|
+
/**
|
|
15
|
+
* Emit the C# LoadContext / SaveContext file.
|
|
16
|
+
*/
|
|
17
|
+
export function emitCSharpContext(namespace) {
|
|
18
|
+
return `// Copyright (c) Microsoft. All rights reserved.
|
|
19
|
+
using System.Text.Json;
|
|
20
|
+
using YamlDotNet.Serialization;
|
|
21
|
+
|
|
22
|
+
#pragma warning disable IDE0130
|
|
23
|
+
namespace ${namespace};
|
|
24
|
+
#pragma warning restore IDE0130
|
|
25
|
+
|
|
26
|
+
/// <summary>
|
|
27
|
+
/// Context for customizing the loading process of agent definitions.
|
|
28
|
+
/// Provides hooks for pre-processing input data before parsing and
|
|
29
|
+
/// post-processing output data after instantiation.
|
|
30
|
+
/// </summary>
|
|
31
|
+
public class LoadContext
|
|
32
|
+
{
|
|
33
|
+
/// <summary>
|
|
34
|
+
/// Optional callback to transform input data before parsing.
|
|
35
|
+
/// </summary>
|
|
36
|
+
public Func<Dictionary<string, object?>, Dictionary<string, object?>>? PreProcess { get; set; }
|
|
37
|
+
|
|
38
|
+
/// <summary>
|
|
39
|
+
/// Optional callback to transform the result after instantiation.
|
|
40
|
+
/// </summary>
|
|
41
|
+
public Func<object, object>? PostProcess { get; set; }
|
|
42
|
+
|
|
43
|
+
/// <summary>
|
|
44
|
+
/// Apply pre-processing to input data if a PreProcess callback is set.
|
|
45
|
+
/// </summary>
|
|
46
|
+
/// <param name="data">The raw input dictionary to process.</param>
|
|
47
|
+
/// <returns>The processed dictionary, or the original if no callback is set.</returns>
|
|
48
|
+
public Dictionary<string, object?> ProcessInput(Dictionary<string, object?> data)
|
|
49
|
+
{
|
|
50
|
+
if (PreProcess is not null)
|
|
51
|
+
{
|
|
52
|
+
return PreProcess(data);
|
|
53
|
+
}
|
|
54
|
+
return data;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/// <summary>
|
|
58
|
+
/// Apply post-processing to the result if a PostProcess callback is set.
|
|
59
|
+
/// </summary>
|
|
60
|
+
/// <typeparam name="T">The type of the result.</typeparam>
|
|
61
|
+
/// <param name="result">The instantiated object to process.</param>
|
|
62
|
+
/// <returns>The processed result, or the original if no callback is set.</returns>
|
|
63
|
+
public T ProcessOutput<T>(T result) where T : class
|
|
64
|
+
{
|
|
65
|
+
if (PostProcess is not null)
|
|
66
|
+
{
|
|
67
|
+
return (T)PostProcess(result);
|
|
68
|
+
}
|
|
69
|
+
return result;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/// <summary>
|
|
74
|
+
/// Context for customizing the serialization process of agent definitions.
|
|
75
|
+
/// Provides hooks for pre-processing the object before serialization and
|
|
76
|
+
/// post-processing the dictionary after serialization.
|
|
77
|
+
/// </summary>
|
|
78
|
+
public class SaveContext
|
|
79
|
+
{
|
|
80
|
+
/// <summary>
|
|
81
|
+
/// Optional callback to transform the object before serialization.
|
|
82
|
+
/// </summary>
|
|
83
|
+
public Func<object, object>? PreSave { get; set; }
|
|
84
|
+
|
|
85
|
+
/// <summary>
|
|
86
|
+
/// Optional callback to transform the dictionary after serialization.
|
|
87
|
+
/// </summary>
|
|
88
|
+
public Func<Dictionary<string, object?>, Dictionary<string, object?>>? PostSave { get; set; }
|
|
89
|
+
|
|
90
|
+
/// <summary>
|
|
91
|
+
/// Output format for collections: "object" (name as key) or "array" (list of dicts).
|
|
92
|
+
/// Defaults to "object".
|
|
93
|
+
/// </summary>
|
|
94
|
+
public string CollectionFormat { get; set; } = "object";
|
|
95
|
+
|
|
96
|
+
/// <summary>
|
|
97
|
+
/// Use shorthand scalar representation when possible (e.g., {"myTool": "function"}).
|
|
98
|
+
/// Defaults to true.
|
|
99
|
+
/// </summary>
|
|
100
|
+
public bool UseShorthand { get; set; } = true;
|
|
101
|
+
|
|
102
|
+
/// <summary>
|
|
103
|
+
/// Apply pre-processing to the object if a PreSave callback is set.
|
|
104
|
+
/// </summary>
|
|
105
|
+
/// <typeparam name="T">The type of the object.</typeparam>
|
|
106
|
+
/// <param name="obj">The object to process before serialization.</param>
|
|
107
|
+
/// <returns>The processed object, or the original if no callback is set.</returns>
|
|
108
|
+
public T ProcessObject<T>(T obj) where T : class
|
|
109
|
+
{
|
|
110
|
+
if (PreSave is not null)
|
|
111
|
+
{
|
|
112
|
+
return (T)PreSave(obj);
|
|
113
|
+
}
|
|
114
|
+
return obj;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/// <summary>
|
|
118
|
+
/// Apply post-processing to the dictionary if a PostSave callback is set.
|
|
119
|
+
/// </summary>
|
|
120
|
+
/// <param name="data">The serialized dictionary to process.</param>
|
|
121
|
+
/// <returns>The processed dictionary, or the original if no callback is set.</returns>
|
|
122
|
+
public Dictionary<string, object?> ProcessDict(Dictionary<string, object?> data)
|
|
123
|
+
{
|
|
124
|
+
if (PostSave is not null)
|
|
125
|
+
{
|
|
126
|
+
return PostSave(data);
|
|
127
|
+
}
|
|
128
|
+
return data;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
private static readonly JsonSerializerOptions s_jsonOptions = new()
|
|
132
|
+
{
|
|
133
|
+
WriteIndented = true,
|
|
134
|
+
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
private static readonly ISerializer s_yamlSerializer = new SerializerBuilder()
|
|
138
|
+
.ConfigureDefaultValuesHandling(DefaultValuesHandling.OmitNull)
|
|
139
|
+
.Build();
|
|
140
|
+
|
|
141
|
+
/// <summary>
|
|
142
|
+
/// Convert the dictionary to a YAML string.
|
|
143
|
+
/// </summary>
|
|
144
|
+
/// <param name="data">The dictionary to convert.</param>
|
|
145
|
+
/// <returns>The YAML string representation.</returns>
|
|
146
|
+
public string ToYaml(Dictionary<string, object?> data)
|
|
147
|
+
{
|
|
148
|
+
return s_yamlSerializer.Serialize(data);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/// <summary>
|
|
152
|
+
/// Convert the dictionary to a JSON string.
|
|
153
|
+
/// </summary>
|
|
154
|
+
/// <param name="data">The dictionary to convert.</param>
|
|
155
|
+
/// <param name="indent">Whether to indent the output. Defaults to true.</param>
|
|
156
|
+
/// <returns>The JSON string representation.</returns>
|
|
157
|
+
public string ToJson(Dictionary<string, object?> data, bool indent = true)
|
|
158
|
+
{
|
|
159
|
+
var options = indent ? s_jsonOptions : new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase };
|
|
160
|
+
return JsonSerializer.Serialize(data, options);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
`;
|
|
164
|
+
}
|
|
165
|
+
// ============================================================================
|
|
166
|
+
// Utils.cs
|
|
167
|
+
// ============================================================================
|
|
168
|
+
/**
|
|
169
|
+
* Emit the C# utility classes file (JsonUtils, YamlUtils, Utils).
|
|
170
|
+
*/
|
|
171
|
+
export function emitCSharpUtils(namespace) {
|
|
172
|
+
return `// Copyright (c) Microsoft. All rights reserved.
|
|
173
|
+
using System.Collections;
|
|
174
|
+
using System.Reflection;
|
|
175
|
+
using System.Text.Json;
|
|
176
|
+
using System.Text.Json.Serialization;
|
|
177
|
+
using YamlDotNet.Serialization;
|
|
178
|
+
using YamlDotNet.Serialization.NamingConventions;
|
|
179
|
+
|
|
180
|
+
#pragma warning disable IDE0130
|
|
181
|
+
namespace ${namespace};
|
|
182
|
+
#pragma warning restore IDE0130
|
|
183
|
+
|
|
184
|
+
/// <summary>
|
|
185
|
+
/// JSON serialization utilities.
|
|
186
|
+
/// </summary>
|
|
187
|
+
public static class JsonUtils
|
|
188
|
+
{
|
|
189
|
+
/// <summary>
|
|
190
|
+
/// Default JSON serializer options with support for nested dictionaries.
|
|
191
|
+
/// </summary>
|
|
192
|
+
public static readonly JsonSerializerOptions Options = new()
|
|
193
|
+
{
|
|
194
|
+
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
|
195
|
+
WriteIndented = true,
|
|
196
|
+
Converters = { new DictionaryJsonConverter() }
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
/// <summary>
|
|
200
|
+
/// Extract a value from a JsonElement.
|
|
201
|
+
/// </summary>
|
|
202
|
+
public static object? GetJsonElementValue(JsonElement element)
|
|
203
|
+
{
|
|
204
|
+
return element.ValueKind switch
|
|
205
|
+
{
|
|
206
|
+
JsonValueKind.String => element.GetString(),
|
|
207
|
+
JsonValueKind.Number => GetNumericValue(element),
|
|
208
|
+
JsonValueKind.True => true,
|
|
209
|
+
JsonValueKind.False => false,
|
|
210
|
+
JsonValueKind.Null => null,
|
|
211
|
+
JsonValueKind.Undefined => null,
|
|
212
|
+
_ => element.GetRawText()
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/// <summary>
|
|
217
|
+
/// Get the appropriate numeric type from a JSON element.
|
|
218
|
+
/// </summary>
|
|
219
|
+
private static object GetNumericValue(JsonElement element)
|
|
220
|
+
{
|
|
221
|
+
// Try int first (most common case for small integers)
|
|
222
|
+
if (element.TryGetInt32(out var i))
|
|
223
|
+
return i;
|
|
224
|
+
// Then try long for larger integers
|
|
225
|
+
if (element.TryGetInt64(out var l))
|
|
226
|
+
return l;
|
|
227
|
+
// Fall back to double for decimals
|
|
228
|
+
return element.GetDouble();
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/// <summary>
|
|
232
|
+
/// Custom converter to properly deserialize nested objects as dictionaries.
|
|
233
|
+
/// </summary>
|
|
234
|
+
private class DictionaryJsonConverter : JsonConverter<Dictionary<string, object?>>
|
|
235
|
+
{
|
|
236
|
+
public override Dictionary<string, object?> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
|
237
|
+
{
|
|
238
|
+
if (reader.TokenType != JsonTokenType.StartObject)
|
|
239
|
+
throw new JsonException("Expected StartObject token");
|
|
240
|
+
|
|
241
|
+
var dict = new Dictionary<string, object?>();
|
|
242
|
+
while (reader.Read())
|
|
243
|
+
{
|
|
244
|
+
if (reader.TokenType == JsonTokenType.EndObject)
|
|
245
|
+
return dict;
|
|
246
|
+
|
|
247
|
+
if (reader.TokenType != JsonTokenType.PropertyName)
|
|
248
|
+
throw new JsonException("Expected PropertyName token");
|
|
249
|
+
|
|
250
|
+
var key = reader.GetString()!;
|
|
251
|
+
reader.Read();
|
|
252
|
+
dict[key] = ReadValue(ref reader, options);
|
|
253
|
+
}
|
|
254
|
+
throw new JsonException("Expected EndObject token");
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
private object? ReadValue(ref Utf8JsonReader reader, JsonSerializerOptions options)
|
|
258
|
+
{
|
|
259
|
+
return reader.TokenType switch
|
|
260
|
+
{
|
|
261
|
+
JsonTokenType.String => reader.GetString(),
|
|
262
|
+
JsonTokenType.Number => GetNumericValue(reader),
|
|
263
|
+
JsonTokenType.True => true,
|
|
264
|
+
JsonTokenType.False => false,
|
|
265
|
+
JsonTokenType.Null => null,
|
|
266
|
+
JsonTokenType.StartObject => Read(ref reader, typeof(Dictionary<string, object?>), options),
|
|
267
|
+
JsonTokenType.StartArray => ReadArray(ref reader, options),
|
|
268
|
+
_ => throw new JsonException($"Unexpected token type: {reader.TokenType}")
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
private static object GetNumericValue(Utf8JsonReader reader)
|
|
273
|
+
{
|
|
274
|
+
// Try int first (most common case for small integers)
|
|
275
|
+
if (reader.TryGetInt32(out var i))
|
|
276
|
+
return i;
|
|
277
|
+
// Then try long for larger integers
|
|
278
|
+
if (reader.TryGetInt64(out var l))
|
|
279
|
+
return l;
|
|
280
|
+
// Fall back to double for decimals
|
|
281
|
+
return reader.GetDouble();
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
private List<object?> ReadArray(ref Utf8JsonReader reader, JsonSerializerOptions options)
|
|
285
|
+
{
|
|
286
|
+
var list = new List<object?>();
|
|
287
|
+
while (reader.Read())
|
|
288
|
+
{
|
|
289
|
+
if (reader.TokenType == JsonTokenType.EndArray)
|
|
290
|
+
return list;
|
|
291
|
+
list.Add(ReadValue(ref reader, options));
|
|
292
|
+
}
|
|
293
|
+
throw new JsonException("Expected EndArray token");
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
public override void Write(Utf8JsonWriter writer, Dictionary<string, object?> value, JsonSerializerOptions options)
|
|
297
|
+
{
|
|
298
|
+
JsonSerializer.Serialize(writer, value, options);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/// <summary>
|
|
304
|
+
/// YAML serialization utilities.
|
|
305
|
+
/// </summary>
|
|
306
|
+
public static class YamlUtils
|
|
307
|
+
{
|
|
308
|
+
/// <summary>
|
|
309
|
+
/// Default YAML deserializer.
|
|
310
|
+
/// </summary>
|
|
311
|
+
public static readonly IDeserializer Deserializer = new DeserializerBuilder()
|
|
312
|
+
.WithNamingConvention(CamelCaseNamingConvention.Instance)
|
|
313
|
+
.Build();
|
|
314
|
+
|
|
315
|
+
/// <summary>
|
|
316
|
+
/// Default YAML serializer.
|
|
317
|
+
/// </summary>
|
|
318
|
+
public static readonly ISerializer Serializer = new SerializerBuilder()
|
|
319
|
+
.WithNamingConvention(CamelCaseNamingConvention.Instance)
|
|
320
|
+
.ConfigureDefaultValuesHandling(DefaultValuesHandling.OmitNull)
|
|
321
|
+
.Build();
|
|
322
|
+
|
|
323
|
+
/// <summary>
|
|
324
|
+
/// Parse a YAML scalar string to a typed value.
|
|
325
|
+
/// Uses YAML deserialization to properly handle quoted strings and types.
|
|
326
|
+
/// Returns the properly typed value (bool, int, double, or string).
|
|
327
|
+
/// </summary>
|
|
328
|
+
public static object? ParseScalar(string yaml)
|
|
329
|
+
{
|
|
330
|
+
// Handle null/empty
|
|
331
|
+
if (string.IsNullOrWhiteSpace(yaml))
|
|
332
|
+
return null;
|
|
333
|
+
|
|
334
|
+
// Use YAML deserializer to properly handle quoted strings and type inference
|
|
335
|
+
try
|
|
336
|
+
{
|
|
337
|
+
var result = Deserializer.Deserialize<object>(yaml);
|
|
338
|
+
// YamlDotNet returns strings for everything when deserializing to object
|
|
339
|
+
// We need to do additional type parsing
|
|
340
|
+
if (result is string str)
|
|
341
|
+
{
|
|
342
|
+
if (str == "null" || str == "~" || str == "")
|
|
343
|
+
return null;
|
|
344
|
+
if (str == "true" || str == "True" || str == "TRUE")
|
|
345
|
+
return true;
|
|
346
|
+
if (str == "false" || str == "False" || str == "FALSE")
|
|
347
|
+
return false;
|
|
348
|
+
if (int.TryParse(str, out var intValue))
|
|
349
|
+
return intValue;
|
|
350
|
+
if (double.TryParse(str, out var doubleValue))
|
|
351
|
+
return doubleValue;
|
|
352
|
+
return str;
|
|
353
|
+
}
|
|
354
|
+
return result;
|
|
355
|
+
}
|
|
356
|
+
catch
|
|
357
|
+
{
|
|
358
|
+
return yaml;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/// <summary>
|
|
364
|
+
/// Utilities for retrieving property values and working with dictionaries.
|
|
365
|
+
/// </summary>
|
|
366
|
+
internal static class Utils
|
|
367
|
+
{
|
|
368
|
+
public static object? GetScalarValue(this JsonElement obj)
|
|
369
|
+
{
|
|
370
|
+
return obj.ValueKind switch
|
|
371
|
+
{
|
|
372
|
+
JsonValueKind.String => obj.GetString(),
|
|
373
|
+
JsonValueKind.Number => obj.GetRawText().Contains('.') ? obj.GetSingle() : obj.GetInt32(),
|
|
374
|
+
JsonValueKind.True => true,
|
|
375
|
+
JsonValueKind.False => false,
|
|
376
|
+
JsonValueKind.Array => obj.EnumerateArray().Select(static x => x.GetScalarValue()).ToArray(),
|
|
377
|
+
JsonValueKind.Null => null,
|
|
378
|
+
JsonValueKind.Object => null,
|
|
379
|
+
JsonValueKind.Undefined => null,
|
|
380
|
+
_ => null,
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/// <summary>
|
|
385
|
+
/// Retrieves a value from the dictionary by key and attempts to convert it to the specified type T.
|
|
386
|
+
/// </summary>
|
|
387
|
+
/// <typeparam name="T">The type to convert the value to.</typeparam>
|
|
388
|
+
/// <param name="dict">The dictionary to search.</param>
|
|
389
|
+
/// <param name="key">The key of the value to retrieve.</param>
|
|
390
|
+
/// <returns>The value converted to type T, or default if not found.</returns>
|
|
391
|
+
public static T? GetValue<T>(this Dictionary<string, object?> dict, string key)
|
|
392
|
+
{
|
|
393
|
+
if (dict.TryGetValue(key, out var value) && value is not null)
|
|
394
|
+
{
|
|
395
|
+
if (value is T typedValue)
|
|
396
|
+
{
|
|
397
|
+
return typedValue;
|
|
398
|
+
}
|
|
399
|
+
try
|
|
400
|
+
{
|
|
401
|
+
return (T)Convert.ChangeType(value, typeof(T));
|
|
402
|
+
}
|
|
403
|
+
catch
|
|
404
|
+
{
|
|
405
|
+
return default;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
return default;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
/// <summary>
|
|
412
|
+
/// Retrieves a nested dictionary from the dictionary by key.
|
|
413
|
+
/// </summary>
|
|
414
|
+
/// <param name="dict">The dictionary to search.</param>
|
|
415
|
+
/// <param name="key">The key of the nested dictionary.</param>
|
|
416
|
+
/// <returns>Dictionary if found; otherwise, an empty dictionary.</returns>
|
|
417
|
+
public static Dictionary<string, object?> GetDictionary(this Dictionary<string, object?> dict, string key)
|
|
418
|
+
{
|
|
419
|
+
if (dict.TryGetValue(key, out var value))
|
|
420
|
+
{
|
|
421
|
+
return value.GetDictionary();
|
|
422
|
+
}
|
|
423
|
+
return new Dictionary<string, object?>();
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
/// <summary>
|
|
427
|
+
/// Retrieves a nested dictionary from any object.
|
|
428
|
+
/// Handles both Dictionary<string, object?> and Dictionary<object, object> (from YAML).
|
|
429
|
+
/// </summary>
|
|
430
|
+
/// <param name="obj">The object that should be a dictionary.</param>
|
|
431
|
+
/// <returns>Dictionary if the object is a dictionary; otherwise, an empty dictionary.</returns>
|
|
432
|
+
public static Dictionary<string, object?> GetDictionary(this object? obj)
|
|
433
|
+
{
|
|
434
|
+
if (obj is Dictionary<string, object?> dict)
|
|
435
|
+
{
|
|
436
|
+
return dict;
|
|
437
|
+
}
|
|
438
|
+
// Handle YAML's Dictionary<object, object>
|
|
439
|
+
if (obj is IDictionary<object, object> objDict)
|
|
440
|
+
{
|
|
441
|
+
return objDict.ToDictionary(
|
|
442
|
+
kvp => kvp.Key?.ToString() ?? string.Empty,
|
|
443
|
+
kvp => (object?)kvp.Value);
|
|
444
|
+
}
|
|
445
|
+
return new Dictionary<string, object?>();
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
/// <summary>
|
|
449
|
+
/// Retrieves a nested dictionary from any object, with shorthand property support.
|
|
450
|
+
/// If the object is not a dictionary and a shorthand property is specified,
|
|
451
|
+
/// wraps the scalar value as { shorthandProperty: value }.
|
|
452
|
+
/// </summary>
|
|
453
|
+
/// <param name="obj">The object that should be a dictionary.</param>
|
|
454
|
+
/// <param name="shorthandProperty">Optional shorthand property name for scalar wrapping.</param>
|
|
455
|
+
/// <returns>Dictionary if the object is a dictionary; shorthand-wrapped dict for scalars; otherwise, an empty dictionary.</returns>
|
|
456
|
+
public static Dictionary<string, object?> GetDictionary(this object? obj, string? shorthandProperty)
|
|
457
|
+
{
|
|
458
|
+
var dict = obj.GetDictionary();
|
|
459
|
+
if (dict.Count > 0) return dict;
|
|
460
|
+
if (shorthandProperty is not null && obj is not null)
|
|
461
|
+
return new Dictionary<string, object?> { [shorthandProperty] = obj };
|
|
462
|
+
return dict;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
/// <summary>
|
|
466
|
+
/// Retrieves a value from the dictionary by key and attempts to convert it to the specified type T.
|
|
467
|
+
/// </summary>
|
|
468
|
+
/// <typeparam name="T">The type to convert the value to.</typeparam>
|
|
469
|
+
/// <param name="dict">The dictionary to search.</param>
|
|
470
|
+
/// <param name="key">The key of the value to retrieve.</param>
|
|
471
|
+
/// <returns></returns>
|
|
472
|
+
public static T? GetValueOrDefault<T>(this IDictionary<string, object> dict, string key)
|
|
473
|
+
{
|
|
474
|
+
// check if T is a class and use .ctor recursively
|
|
475
|
+
if (dict.TryGetValue(key, out var value))
|
|
476
|
+
{
|
|
477
|
+
return (T?)Convert.ChangeType(value, typeof(T));
|
|
478
|
+
}
|
|
479
|
+
return default;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
/// <summary>
|
|
483
|
+
/// Converts a named dictionary or list of dictionaries into a list of dictionaries (for normalizing Named objects into List objects).
|
|
484
|
+
/// </summary>
|
|
485
|
+
/// <param name="data"></param>
|
|
486
|
+
/// <returns>List of dictionaries</returns>
|
|
487
|
+
public static IList<IDictionary<string, object>> GetNamedDictionaryList(this object data)
|
|
488
|
+
{
|
|
489
|
+
if (data is IDictionary<string, object> dict)
|
|
490
|
+
{
|
|
491
|
+
return [.. dict
|
|
492
|
+
.Where(kvp => kvp.Value is IDictionary<string, object>)
|
|
493
|
+
.Select(kvp =>
|
|
494
|
+
{
|
|
495
|
+
var newDict = new Dictionary<string, object>((IDictionary<string, object>)kvp.Value!)
|
|
496
|
+
{
|
|
497
|
+
{ "name", kvp.Key }
|
|
498
|
+
};
|
|
499
|
+
return (IDictionary<string, object>)newDict;
|
|
500
|
+
})];
|
|
501
|
+
}
|
|
502
|
+
if (data is IEnumerable<object> enumerable)
|
|
503
|
+
{
|
|
504
|
+
return [.. enumerable.OfType<IDictionary<string, object>>()];
|
|
505
|
+
}
|
|
506
|
+
return [];
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
/// <summary>
|
|
510
|
+
/// Retrieves a nested dictionary from the dictionary by key.
|
|
511
|
+
/// </summary>
|
|
512
|
+
/// <param name="dict">The dictionary to search.</param>
|
|
513
|
+
/// <param name="key">The key of the nested dictionary.</param>
|
|
514
|
+
/// <returns>Dictionary<string, object> if found; otherwise, an empty dictionary.</returns>
|
|
515
|
+
public static IDictionary<string, object> GetDictionaryOrDefault(this IDictionary<string, object> dict, string key)
|
|
516
|
+
{
|
|
517
|
+
if (dict.TryGetValue(key, out var value) && value is IDictionary<string, object> nestedDict)
|
|
518
|
+
{
|
|
519
|
+
return nestedDict;
|
|
520
|
+
}
|
|
521
|
+
return new Dictionary<string, object>();
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
/// <summary>
|
|
525
|
+
/// Expands a dictionary by converting its keys and values to strings and more usable formats.
|
|
526
|
+
/// </summary>
|
|
527
|
+
/// <param name="dictionary">The dictionary to expand.</param>
|
|
528
|
+
/// <returns>A new dictionary with expanded keys and values.</returns>
|
|
529
|
+
private static Dictionary<string, object> Expand(IDictionary dictionary)
|
|
530
|
+
{
|
|
531
|
+
var dict = new Dictionary<string, object>();
|
|
532
|
+
foreach (DictionaryEntry entry in dictionary)
|
|
533
|
+
{
|
|
534
|
+
if (entry.Value != null)
|
|
535
|
+
dict.Add(entry.Key.ToString()!, GetValue(entry.Value));
|
|
536
|
+
}
|
|
537
|
+
return dict;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
/// <summary>
|
|
541
|
+
/// Expands a dictionary by converting its values to a more usable format.
|
|
542
|
+
/// </summary>
|
|
543
|
+
/// <param name="o">The object to convert.</param>
|
|
544
|
+
/// <returns>A more usable object.</returns>
|
|
545
|
+
private static object GetValue(object o)
|
|
546
|
+
{
|
|
547
|
+
return Type.GetTypeCode(o.GetType()) switch
|
|
548
|
+
{
|
|
549
|
+
TypeCode.Object => o switch
|
|
550
|
+
{
|
|
551
|
+
|
|
552
|
+
IDictionary dict => Expand(dict),
|
|
553
|
+
IList list => Enumerable.Range(0, list.Count).Where(i => list[i] != null).Select(i => list[i]!.ToParamDictionary()).ToArray(),
|
|
554
|
+
_ => o.ToParamDictionary(),
|
|
555
|
+
},
|
|
556
|
+
_ => o,
|
|
557
|
+
};
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
/// <summary>
|
|
561
|
+
/// Converts an object to a dictionary of parameters.
|
|
562
|
+
/// </summary>
|
|
563
|
+
/// <param name="obj">The object to convert.</param>
|
|
564
|
+
/// <returns>A dictionary of parameters.</returns>
|
|
565
|
+
public static IDictionary<string, object> ToParamDictionary(this object obj)
|
|
566
|
+
{
|
|
567
|
+
if (obj == null)
|
|
568
|
+
return new Dictionary<string, object>();
|
|
569
|
+
|
|
570
|
+
else if (obj is IDictionary<string, object> dictionary)
|
|
571
|
+
return dictionary;
|
|
572
|
+
|
|
573
|
+
var items = obj.GetType()
|
|
574
|
+
.GetProperties(BindingFlags.Public | BindingFlags.Instance)
|
|
575
|
+
.Where(prop => prop.GetGetMethod() != null);
|
|
576
|
+
|
|
577
|
+
var dict = new Dictionary<string, object>();
|
|
578
|
+
|
|
579
|
+
foreach (var item in items)
|
|
580
|
+
{
|
|
581
|
+
var value = item.GetValue(obj);
|
|
582
|
+
if (value != null)
|
|
583
|
+
dict.Add(item.Name, GetValue(value));
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
return dict;
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
`;
|
|
590
|
+
}
|
|
591
|
+
//# sourceMappingURL=scaffolding.js.map
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* C# test emitter — TypeNode → xUnit test file.
|
|
3
|
+
*
|
|
4
|
+
* Replaces `test.cs.njk` Nunjucks template with a typed TypeScript function
|
|
5
|
+
* that produces a complete C# xUnit test class.
|
|
6
|
+
*
|
|
7
|
+
* Each TypeNode with samples/coercions/factories gets one test file
|
|
8
|
+
* containing LoadYaml, LoadJson, roundtrip, and validity tests.
|
|
9
|
+
*/
|
|
10
|
+
import { FactoryEntry } from "../../decorators.js";
|
|
11
|
+
import { TypeNode } from "../../ir/ast.js";
|
|
12
|
+
export interface CSharpTestContext {
|
|
13
|
+
node: TypeNode;
|
|
14
|
+
namespace: string;
|
|
15
|
+
examples: Array<{
|
|
16
|
+
json: string[];
|
|
17
|
+
yaml: string[];
|
|
18
|
+
validations: Array<{
|
|
19
|
+
key: string;
|
|
20
|
+
value: any;
|
|
21
|
+
startDelim: string;
|
|
22
|
+
endDelim: string;
|
|
23
|
+
}>;
|
|
24
|
+
}>;
|
|
25
|
+
coercions: Array<{
|
|
26
|
+
title: string;
|
|
27
|
+
scalar: string;
|
|
28
|
+
value: string | number;
|
|
29
|
+
validations: Array<{
|
|
30
|
+
key: string;
|
|
31
|
+
value: any;
|
|
32
|
+
delimiter: string;
|
|
33
|
+
}>;
|
|
34
|
+
}>;
|
|
35
|
+
factories: FactoryEntry[];
|
|
36
|
+
renderName: (name: string) => string;
|
|
37
|
+
renderCsharpFactoryMethodName: (factoryName: string) => string;
|
|
38
|
+
renderCsharpFactoryTestValue: (typeStr: string) => string;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Emit a complete C# xUnit test file for a type node.
|
|
42
|
+
*/
|
|
43
|
+
export declare function emitCSharpTest(ctx: CSharpTestContext): string;
|