@typespec/emitter-framework 0.9.0-dev.1 → 0.9.0-dev.3
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/csharp/components/class-declaration.d.ts +9 -0
- package/dist/src/csharp/components/class-declaration.d.ts.map +1 -0
- package/dist/src/csharp/components/class-declaration.js +97 -0
- package/dist/src/csharp/components/enum-declaration.d.ts +9 -0
- package/dist/src/csharp/components/enum-declaration.d.ts.map +1 -0
- package/dist/src/csharp/components/enum-declaration.js +52 -0
- package/dist/src/csharp/components/index.d.ts +4 -0
- package/dist/src/csharp/components/index.d.ts.map +1 -0
- package/dist/src/csharp/components/index.js +3 -0
- package/dist/src/csharp/components/type-expression.d.ts +11 -0
- package/dist/src/csharp/components/type-expression.d.ts.map +1 -0
- package/dist/src/csharp/components/type-expression.js +129 -0
- package/dist/src/csharp/components/utils/refkey.d.ts +23 -0
- package/dist/src/csharp/components/utils/refkey.d.ts.map +1 -0
- package/dist/src/csharp/components/utils/refkey.js +35 -0
- package/dist/src/csharp/index.d.ts +2 -0
- package/dist/src/csharp/index.d.ts.map +1 -0
- package/dist/src/csharp/index.js +1 -0
- package/dist/test/csharp/components/class-declaration.test.d.ts +2 -0
- package/dist/test/csharp/components/class-declaration.test.d.ts.map +1 -0
- package/dist/test/csharp/components/class-declaration.test.js +421 -0
- package/dist/test/csharp/components/enum-declaration.test.d.ts +2 -0
- package/dist/test/csharp/components/enum-declaration.test.d.ts.map +1 -0
- package/dist/test/csharp/components/enum-declaration.test.js +355 -0
- package/dist/test/csharp/test-host.d.ts +11 -0
- package/dist/test/csharp/test-host.d.ts.map +1 -0
- package/dist/test/csharp/test-host.js +32 -0
- package/dist/test/csharp/utils.d.ts +3 -0
- package/dist/test/csharp/utils.d.ts.map +1 -0
- package/dist/test/csharp/utils.js +6 -0
- package/package.json +5 -1
- package/src/csharp/components/class-declaration.tsx +87 -0
- package/src/csharp/components/enum-declaration.tsx +48 -0
- package/src/csharp/components/index.ts +3 -0
- package/src/csharp/components/type-expression.tsx +112 -0
- package/src/csharp/components/utils/refkey.ts +36 -0
- package/src/csharp/index.ts +1 -0
- package/test/csharp/components/class-declaration.test.tsx +344 -0
- package/test/csharp/components/enum-declaration.test.tsx +296 -0
- package/test/csharp/test-host.ts +42 -0
- package/test/csharp/utils.ts +8 -0
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
import { createComponent as _$createComponent, createIntrinsic as _$createIntrinsic } from "@alloy-js/core/jsx-runtime";
|
|
2
|
+
import { render } from "@alloy-js/core";
|
|
3
|
+
import { d } from "@alloy-js/core/testing";
|
|
4
|
+
import * as cs from "@alloy-js/csharp";
|
|
5
|
+
import { Namespace, SourceFile } from "@alloy-js/csharp";
|
|
6
|
+
import { beforeEach, it } from "vitest";
|
|
7
|
+
import { Output } from "../../../src/core/index.js";
|
|
8
|
+
import { EnumDeclaration } from "../../../src/csharp/index.js";
|
|
9
|
+
import { createEmitterFrameworkTestRunner } from "../test-host.js";
|
|
10
|
+
import { assertFileContents } from "../utils.js";
|
|
11
|
+
let runner;
|
|
12
|
+
beforeEach(async () => {
|
|
13
|
+
runner = await createEmitterFrameworkTestRunner();
|
|
14
|
+
});
|
|
15
|
+
it("renders a basic enum declaration", async () => {
|
|
16
|
+
const {
|
|
17
|
+
TestEnum
|
|
18
|
+
} = await runner.compile(`
|
|
19
|
+
@test enum TestEnum {
|
|
20
|
+
Value1;
|
|
21
|
+
Value2;
|
|
22
|
+
Value3;
|
|
23
|
+
}
|
|
24
|
+
`);
|
|
25
|
+
const res = render(_$createComponent(Output, {
|
|
26
|
+
get program() {
|
|
27
|
+
return runner.program;
|
|
28
|
+
},
|
|
29
|
+
get children() {
|
|
30
|
+
return _$createComponent(Namespace, {
|
|
31
|
+
name: "TestNamespace",
|
|
32
|
+
get children() {
|
|
33
|
+
return _$createComponent(SourceFile, {
|
|
34
|
+
path: "test.cs",
|
|
35
|
+
get children() {
|
|
36
|
+
return _$createComponent(EnumDeclaration, {
|
|
37
|
+
type: TestEnum
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
}));
|
|
45
|
+
assertFileContents(res, d`
|
|
46
|
+
namespace TestNamespace
|
|
47
|
+
{
|
|
48
|
+
public enum TestEnum
|
|
49
|
+
{
|
|
50
|
+
Value1,
|
|
51
|
+
Value2,
|
|
52
|
+
Value3
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
`);
|
|
56
|
+
});
|
|
57
|
+
it("renders an empty enum declaration", async () => {
|
|
58
|
+
const {
|
|
59
|
+
TestEnum
|
|
60
|
+
} = await runner.compile(`
|
|
61
|
+
@test enum TestEnum {}
|
|
62
|
+
`);
|
|
63
|
+
const res = render(_$createComponent(Output, {
|
|
64
|
+
get program() {
|
|
65
|
+
return runner.program;
|
|
66
|
+
},
|
|
67
|
+
get children() {
|
|
68
|
+
return _$createComponent(Namespace, {
|
|
69
|
+
name: "TestNamespace",
|
|
70
|
+
get children() {
|
|
71
|
+
return _$createComponent(SourceFile, {
|
|
72
|
+
path: "test.cs",
|
|
73
|
+
get children() {
|
|
74
|
+
return _$createComponent(EnumDeclaration, {
|
|
75
|
+
type: TestEnum
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
}));
|
|
83
|
+
assertFileContents(res, d`
|
|
84
|
+
namespace TestNamespace
|
|
85
|
+
{
|
|
86
|
+
public enum TestEnum
|
|
87
|
+
{
|
|
88
|
+
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
`);
|
|
92
|
+
});
|
|
93
|
+
it("can override enum name", async () => {
|
|
94
|
+
const {
|
|
95
|
+
TestEnum
|
|
96
|
+
} = await runner.compile(`
|
|
97
|
+
@test enum TestEnum {
|
|
98
|
+
Value1;
|
|
99
|
+
Value2;
|
|
100
|
+
}
|
|
101
|
+
`);
|
|
102
|
+
const res = render(_$createComponent(Output, {
|
|
103
|
+
get program() {
|
|
104
|
+
return runner.program;
|
|
105
|
+
},
|
|
106
|
+
get children() {
|
|
107
|
+
return _$createComponent(Namespace, {
|
|
108
|
+
name: "TestNamespace",
|
|
109
|
+
get children() {
|
|
110
|
+
return _$createComponent(SourceFile, {
|
|
111
|
+
path: "test.cs",
|
|
112
|
+
get children() {
|
|
113
|
+
return _$createComponent(EnumDeclaration, {
|
|
114
|
+
type: TestEnum,
|
|
115
|
+
name: "CustomEnumName"
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
}));
|
|
123
|
+
assertFileContents(res, d`
|
|
124
|
+
namespace TestNamespace
|
|
125
|
+
{
|
|
126
|
+
public enum CustomEnumName
|
|
127
|
+
{
|
|
128
|
+
Value1,
|
|
129
|
+
Value2
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
`);
|
|
133
|
+
});
|
|
134
|
+
it("renders an enum with access modifiers", async () => {
|
|
135
|
+
const {
|
|
136
|
+
TestEnum
|
|
137
|
+
} = await runner.compile(`
|
|
138
|
+
@test enum TestEnum {
|
|
139
|
+
Value1;
|
|
140
|
+
Value2;
|
|
141
|
+
}
|
|
142
|
+
`);
|
|
143
|
+
const res = render(_$createComponent(Output, {
|
|
144
|
+
get program() {
|
|
145
|
+
return runner.program;
|
|
146
|
+
},
|
|
147
|
+
get children() {
|
|
148
|
+
return _$createComponent(Namespace, {
|
|
149
|
+
name: "TestNamespace",
|
|
150
|
+
get children() {
|
|
151
|
+
return _$createComponent(SourceFile, {
|
|
152
|
+
path: "test.cs",
|
|
153
|
+
get children() {
|
|
154
|
+
return _$createComponent(EnumDeclaration, {
|
|
155
|
+
type: TestEnum,
|
|
156
|
+
accessModifier: "internal"
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
}));
|
|
164
|
+
assertFileContents(res, d`
|
|
165
|
+
namespace TestNamespace
|
|
166
|
+
{
|
|
167
|
+
internal enum TestEnum
|
|
168
|
+
{
|
|
169
|
+
Value1,
|
|
170
|
+
Value2
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
`);
|
|
174
|
+
});
|
|
175
|
+
it("renders enum with C# naming conventions", async () => {
|
|
176
|
+
const {
|
|
177
|
+
TestEnum
|
|
178
|
+
} = await runner.compile(`
|
|
179
|
+
@test enum TestEnum {
|
|
180
|
+
value_one;
|
|
181
|
+
value_two;
|
|
182
|
+
value_three;
|
|
183
|
+
}
|
|
184
|
+
`);
|
|
185
|
+
const res = render(_$createComponent(Output, {
|
|
186
|
+
get program() {
|
|
187
|
+
return runner.program;
|
|
188
|
+
},
|
|
189
|
+
get namePolicy() {
|
|
190
|
+
return cs.createCSharpNamePolicy();
|
|
191
|
+
},
|
|
192
|
+
get children() {
|
|
193
|
+
return _$createComponent(Namespace, {
|
|
194
|
+
name: "TestNamespace",
|
|
195
|
+
get children() {
|
|
196
|
+
return _$createComponent(SourceFile, {
|
|
197
|
+
path: "test.cs",
|
|
198
|
+
get children() {
|
|
199
|
+
return _$createComponent(EnumDeclaration, {
|
|
200
|
+
type: TestEnum
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
}));
|
|
208
|
+
assertFileContents(res, d`
|
|
209
|
+
namespace TestNamespace
|
|
210
|
+
{
|
|
211
|
+
public enum TestEnum
|
|
212
|
+
{
|
|
213
|
+
ValueOne,
|
|
214
|
+
ValueTwo,
|
|
215
|
+
ValueThree
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
`);
|
|
219
|
+
});
|
|
220
|
+
it("renders enum with single value", async () => {
|
|
221
|
+
const {
|
|
222
|
+
TestEnum
|
|
223
|
+
} = await runner.compile(`
|
|
224
|
+
@test enum TestEnum {
|
|
225
|
+
OnlyValue;
|
|
226
|
+
}
|
|
227
|
+
`);
|
|
228
|
+
const res = render(_$createComponent(Output, {
|
|
229
|
+
get program() {
|
|
230
|
+
return runner.program;
|
|
231
|
+
},
|
|
232
|
+
get children() {
|
|
233
|
+
return _$createComponent(Namespace, {
|
|
234
|
+
name: "TestNamespace",
|
|
235
|
+
get children() {
|
|
236
|
+
return _$createComponent(SourceFile, {
|
|
237
|
+
path: "test.cs",
|
|
238
|
+
get children() {
|
|
239
|
+
return _$createComponent(EnumDeclaration, {
|
|
240
|
+
type: TestEnum
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
}));
|
|
248
|
+
assertFileContents(res, d`
|
|
249
|
+
namespace TestNamespace
|
|
250
|
+
{
|
|
251
|
+
public enum TestEnum
|
|
252
|
+
{
|
|
253
|
+
OnlyValue
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
`);
|
|
257
|
+
});
|
|
258
|
+
it("renders enum with numeric-like member names", async () => {
|
|
259
|
+
const {
|
|
260
|
+
TestEnum
|
|
261
|
+
} = await runner.compile(`
|
|
262
|
+
@test enum TestEnum {
|
|
263
|
+
Value0;
|
|
264
|
+
Value1;
|
|
265
|
+
Value10;
|
|
266
|
+
Value100;
|
|
267
|
+
}
|
|
268
|
+
`);
|
|
269
|
+
const res = render(_$createComponent(Output, {
|
|
270
|
+
get program() {
|
|
271
|
+
return runner.program;
|
|
272
|
+
},
|
|
273
|
+
get children() {
|
|
274
|
+
return _$createComponent(Namespace, {
|
|
275
|
+
name: "TestNamespace",
|
|
276
|
+
get children() {
|
|
277
|
+
return _$createComponent(SourceFile, {
|
|
278
|
+
path: "test.cs",
|
|
279
|
+
get children() {
|
|
280
|
+
return _$createComponent(EnumDeclaration, {
|
|
281
|
+
type: TestEnum
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
}));
|
|
289
|
+
assertFileContents(res, d`
|
|
290
|
+
namespace TestNamespace
|
|
291
|
+
{
|
|
292
|
+
public enum TestEnum
|
|
293
|
+
{
|
|
294
|
+
Value0,
|
|
295
|
+
Value1,
|
|
296
|
+
Value10,
|
|
297
|
+
Value100
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
`);
|
|
301
|
+
});
|
|
302
|
+
it("renders multiple enums in the same namespace", async () => {
|
|
303
|
+
const {
|
|
304
|
+
TestEnum1,
|
|
305
|
+
TestEnum2
|
|
306
|
+
} = await runner.compile(`
|
|
307
|
+
@test enum TestEnum1 {
|
|
308
|
+
Value1;
|
|
309
|
+
Value2;
|
|
310
|
+
}
|
|
311
|
+
@test enum TestEnum2 {
|
|
312
|
+
OptionA;
|
|
313
|
+
OptionB;
|
|
314
|
+
OptionC;
|
|
315
|
+
}
|
|
316
|
+
`);
|
|
317
|
+
const res = render(_$createComponent(Output, {
|
|
318
|
+
get program() {
|
|
319
|
+
return runner.program;
|
|
320
|
+
},
|
|
321
|
+
get children() {
|
|
322
|
+
return _$createComponent(Namespace, {
|
|
323
|
+
name: "TestNamespace",
|
|
324
|
+
get children() {
|
|
325
|
+
return _$createComponent(SourceFile, {
|
|
326
|
+
path: "test.cs",
|
|
327
|
+
get children() {
|
|
328
|
+
return [_$createComponent(EnumDeclaration, {
|
|
329
|
+
type: TestEnum1
|
|
330
|
+
}), _$createIntrinsic("hbr", {}), _$createComponent(EnumDeclaration, {
|
|
331
|
+
type: TestEnum2
|
|
332
|
+
})];
|
|
333
|
+
}
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
}));
|
|
339
|
+
assertFileContents(res, d`
|
|
340
|
+
namespace TestNamespace
|
|
341
|
+
{
|
|
342
|
+
public enum TestEnum1
|
|
343
|
+
{
|
|
344
|
+
Value1,
|
|
345
|
+
Value2
|
|
346
|
+
}
|
|
347
|
+
public enum TestEnum2
|
|
348
|
+
{
|
|
349
|
+
OptionA,
|
|
350
|
+
OptionB,
|
|
351
|
+
OptionC
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
`);
|
|
355
|
+
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Program } from "@typespec/compiler";
|
|
2
|
+
export declare function createTypespecCliTestHost(options?: {
|
|
3
|
+
libraries: "Http"[];
|
|
4
|
+
}): Promise<import("@typespec/compiler/testing").TestHost>;
|
|
5
|
+
export declare function createEmitterFrameworkTestRunner(options?: {
|
|
6
|
+
autoUsings?: string[];
|
|
7
|
+
}): Promise<import("@typespec/compiler/testing").BasicTestRunner>;
|
|
8
|
+
export declare function getProgram(code: string, options?: {
|
|
9
|
+
libraries: "Http"[];
|
|
10
|
+
}): Promise<Program>;
|
|
11
|
+
//# sourceMappingURL=test-host.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test-host.d.ts","sourceRoot":"","sources":["../../../test/csharp/test-host.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAS7C,wBAAsB,yBAAyB,CAC7C,OAAO,GAAE;IAAE,SAAS,EAAE,MAAM,EAAE,CAAA;CAAsB,0DASrD;AAED,wBAAsB,gCAAgC,CAAC,OAAO,GAAE;IAAE,UAAU,CAAC,EAAE,MAAM,EAAE,CAAA;CAAO,iEAK7F;AAED,wBAAsB,UAAU,CAC9B,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE;IAAE,SAAS,EAAE,MAAM,EAAE,CAAA;CAAsB,GACnD,OAAO,CAAC,OAAO,CAAC,CAUlB"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { createTestHost, createTestWrapper, expectDiagnosticEmpty } from "@typespec/compiler/testing";
|
|
2
|
+
import { HttpTestLibrary } from "@typespec/http/testing";
|
|
3
|
+
export async function createTypespecCliTestHost(options = {
|
|
4
|
+
libraries: []
|
|
5
|
+
}) {
|
|
6
|
+
const libraries = [];
|
|
7
|
+
if (options.libraries.includes("Http")) {
|
|
8
|
+
libraries.push(HttpTestLibrary);
|
|
9
|
+
}
|
|
10
|
+
return createTestHost({
|
|
11
|
+
libraries
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
export async function createEmitterFrameworkTestRunner(options = {}) {
|
|
15
|
+
const host = await createTypespecCliTestHost();
|
|
16
|
+
return createTestWrapper(host, {
|
|
17
|
+
autoUsings: options.autoUsings
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
export async function getProgram(code, options = {
|
|
21
|
+
libraries: []
|
|
22
|
+
}) {
|
|
23
|
+
const host = await createTypespecCliTestHost(options);
|
|
24
|
+
const wrapper = createTestWrapper(host, {
|
|
25
|
+
compilerOptions: {
|
|
26
|
+
noEmit: true
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
const [_, diagnostics] = await wrapper.compileAndDiagnose(code);
|
|
30
|
+
expectDiagnosticEmpty(diagnostics);
|
|
31
|
+
return wrapper.program;
|
|
32
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../test/csharp/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAGjD,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,QAIxE"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@typespec/emitter-framework",
|
|
3
|
-
"version": "0.9.0-dev.
|
|
3
|
+
"version": "0.9.0-dev.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"repository": {
|
|
@@ -11,6 +11,9 @@
|
|
|
11
11
|
".": {
|
|
12
12
|
"import": "./dist/src/core/index.js"
|
|
13
13
|
},
|
|
14
|
+
"./csharp": {
|
|
15
|
+
"import": "./dist/src/csharp/index.js"
|
|
16
|
+
},
|
|
14
17
|
"./typescript": {
|
|
15
18
|
"import": "./dist/src/typescript/index.js"
|
|
16
19
|
},
|
|
@@ -25,6 +28,7 @@
|
|
|
25
28
|
"peerDependencies": {
|
|
26
29
|
"@alloy-js/core": "^0.17.0",
|
|
27
30
|
"@alloy-js/typescript": "^0.17.0",
|
|
31
|
+
"@alloy-js/csharp": "^0.17.0",
|
|
28
32
|
"@typespec/compiler": "^1.1.0",
|
|
29
33
|
"@typespec/http": "^1.1.0",
|
|
30
34
|
"@typespec/rest": "^0.71.0 || >=0.72.0-dev <0.72.0"
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import * as ay from "@alloy-js/core";
|
|
2
|
+
import * as cs from "@alloy-js/csharp";
|
|
3
|
+
import { Interface, Model } from "@typespec/compiler";
|
|
4
|
+
import { useTsp } from "../../core/index.js";
|
|
5
|
+
import { TypeExpression } from "./type-expression.jsx";
|
|
6
|
+
import { declarationRefkeys } from "./utils/refkey.js";
|
|
7
|
+
|
|
8
|
+
export interface ClassDeclarationProps extends Omit<cs.ClassProps, "name"> {
|
|
9
|
+
name?: string;
|
|
10
|
+
type: Model | Interface;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
interface ClassPropertiesProps {
|
|
14
|
+
type: Model;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
interface ClassMethodsProps {
|
|
18
|
+
type: Interface;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function ClassDeclaration(props: ClassDeclarationProps): ay.Children {
|
|
22
|
+
const { $ } = useTsp();
|
|
23
|
+
|
|
24
|
+
const namePolicy = cs.useCSharpNamePolicy();
|
|
25
|
+
const className = props.name ?? namePolicy.getName(props.type.name, "class");
|
|
26
|
+
|
|
27
|
+
const refkeys = declarationRefkeys(props.refkey, props.type)[0]; // TODO: support multiple refkeys for declarations in alloy
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<>
|
|
31
|
+
<cs.Class name={className} accessModifier={props.accessModifier} refkey={refkeys}>
|
|
32
|
+
{$.model.is(props.type) && <ClassProperties type={props.type} />}
|
|
33
|
+
{props.type.kind === "Interface" && <ClassMethods type={props.type} />}
|
|
34
|
+
</cs.Class>
|
|
35
|
+
</>
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function ClassProperties(props: ClassPropertiesProps): ay.Children {
|
|
40
|
+
const namePolicy = cs.useCSharpNamePolicy();
|
|
41
|
+
|
|
42
|
+
const classProperties: ay.Children = [];
|
|
43
|
+
for (const [name, prop] of props.type.properties) {
|
|
44
|
+
classProperties.push(
|
|
45
|
+
<>
|
|
46
|
+
<cs.ClassMember
|
|
47
|
+
name={namePolicy.getName(name, "class-member-public")}
|
|
48
|
+
type={<TypeExpression type={prop.type} />}
|
|
49
|
+
accessModifier="public"
|
|
50
|
+
/>{" "}
|
|
51
|
+
<ay.Block newline>
|
|
52
|
+
<ay.StatementList children={["get", "set"]} />
|
|
53
|
+
</ay.Block>
|
|
54
|
+
</>,
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return (
|
|
59
|
+
<ay.For each={classProperties} hardline>
|
|
60
|
+
{(c) => c}
|
|
61
|
+
</ay.For>
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function ClassMethods(props: ClassMethodsProps): ay.Children {
|
|
66
|
+
const namePolicy = cs.useCSharpNamePolicy();
|
|
67
|
+
|
|
68
|
+
const abstractMethods: ay.Children = [];
|
|
69
|
+
for (const [name, prop] of props.type.operations) {
|
|
70
|
+
abstractMethods.push(
|
|
71
|
+
<cs.ClassMethod
|
|
72
|
+
name={namePolicy.getName(name, "class-method")}
|
|
73
|
+
methodModifier="abstract"
|
|
74
|
+
parameters={[...prop.parameters.properties.entries()].map(([name, prop]) => {
|
|
75
|
+
return {
|
|
76
|
+
name: namePolicy.getName(name, "type-parameter"),
|
|
77
|
+
type: <TypeExpression type={prop.type} />,
|
|
78
|
+
};
|
|
79
|
+
})}
|
|
80
|
+
accessModifier="public"
|
|
81
|
+
returns={<TypeExpression type={prop.returnType} />}
|
|
82
|
+
/>,
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return <>{abstractMethods}</>;
|
|
87
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import * as ay from "@alloy-js/core";
|
|
2
|
+
import * as cs from "@alloy-js/csharp";
|
|
3
|
+
import { Enum, Union } from "@typespec/compiler";
|
|
4
|
+
import { useTsp } from "../../core/index.js";
|
|
5
|
+
import { reportDiagnostic } from "../../lib.js";
|
|
6
|
+
import { declarationRefkeys, efRefkey } from "./utils/refkey.js";
|
|
7
|
+
|
|
8
|
+
export interface EnumDeclarationProps extends Omit<cs.EnumProps, "name"> {
|
|
9
|
+
name?: string;
|
|
10
|
+
type: Union | Enum;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function EnumDeclaration(props: EnumDeclarationProps): ay.Children {
|
|
14
|
+
const { $ } = useTsp();
|
|
15
|
+
let type: Enum;
|
|
16
|
+
if ($.union.is(props.type)) {
|
|
17
|
+
if (!$.union.isValidEnum(props.type)) {
|
|
18
|
+
throw new Error("The provided union type cannot be represented as an enum");
|
|
19
|
+
}
|
|
20
|
+
type = $.enum.createFromUnion(props.type);
|
|
21
|
+
} else {
|
|
22
|
+
type = props.type;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (!props.type.name) {
|
|
26
|
+
reportDiagnostic($.program, { code: "type-declaration-missing-name", target: props.type });
|
|
27
|
+
}
|
|
28
|
+
const refkeys = declarationRefkeys(props.refkey, props.type)[0]; // TODO: support multiple refkeys for declarations in alloy
|
|
29
|
+
const name = props.name ?? cs.useCSharpNamePolicy().getName(props.type.name!, "enum");
|
|
30
|
+
const members = Array.from(type.members.entries());
|
|
31
|
+
|
|
32
|
+
return (
|
|
33
|
+
<cs.Enum name={name} refkey={refkeys} accessModifier={props.accessModifier ?? "public"}>
|
|
34
|
+
<ay.For each={members} joiner={",\n"}>
|
|
35
|
+
{([key, value]) => {
|
|
36
|
+
return (
|
|
37
|
+
<cs.EnumMember
|
|
38
|
+
name={cs.useCSharpNamePolicy().getName(key, "enum-member")}
|
|
39
|
+
refkey={
|
|
40
|
+
$.union.is(props.type) ? efRefkey(props.type.variants.get(key)) : efRefkey(value)
|
|
41
|
+
}
|
|
42
|
+
/>
|
|
43
|
+
);
|
|
44
|
+
}}
|
|
45
|
+
</ay.For>
|
|
46
|
+
</cs.Enum>
|
|
47
|
+
);
|
|
48
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { Children } from "@alloy-js/core";
|
|
2
|
+
import { Reference } from "@alloy-js/csharp";
|
|
3
|
+
import { IntrinsicType, Scalar, Type } from "@typespec/compiler";
|
|
4
|
+
import { Typekit } from "@typespec/compiler/typekit";
|
|
5
|
+
import { useTsp } from "../../core/index.js";
|
|
6
|
+
import { reportTypescriptDiagnostic } from "../../typescript/lib.js";
|
|
7
|
+
import { efRefkey } from "./utils/refkey.js";
|
|
8
|
+
|
|
9
|
+
export interface TypeExpressionProps {
|
|
10
|
+
type: Type;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function TypeExpression(props: TypeExpressionProps): Children {
|
|
14
|
+
const { $ } = useTsp();
|
|
15
|
+
if (isDeclaration($, props.type)) {
|
|
16
|
+
return <Reference refkey={efRefkey(props.type)} />;
|
|
17
|
+
}
|
|
18
|
+
if ($.scalar.is(props.type)) {
|
|
19
|
+
return getScalarIntrinsicExpression($, props.type);
|
|
20
|
+
}
|
|
21
|
+
throw new Error("not implemented");
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const intrinsicNameToCSharpType = new Map<string, string | null>([
|
|
25
|
+
// Core types
|
|
26
|
+
["unknown", "object"], // Matches C#'s `object`
|
|
27
|
+
["string", "string"], // Matches C#'s `string`
|
|
28
|
+
["boolean", "bool"], // Matches C#'s `bool`
|
|
29
|
+
["null", "null"], // Matches C#'s `null`
|
|
30
|
+
["void", "void"], // Matches C#'s `void`
|
|
31
|
+
["never", null], // No direct equivalent in C#
|
|
32
|
+
["bytes", "byte[]"], // Matches C#'s `byte[]`
|
|
33
|
+
|
|
34
|
+
// Numeric types
|
|
35
|
+
["numeric", "decimal"], // Parent type for all numeric types, use most precise
|
|
36
|
+
["integer", "int"], // Broad integer category, maps to `int`
|
|
37
|
+
["float", "float"], // Broad float category, maps to `float`
|
|
38
|
+
["decimal", "decimal"], // Broad decimal category, maps to `decimal`
|
|
39
|
+
["decimal128", "decimal"], // C#'s decimal is 128-bit
|
|
40
|
+
["int64", "long"], // 64-bit signed integer
|
|
41
|
+
["int32", "int"], // 32-bit signed integer
|
|
42
|
+
["int16", "short"], // 16-bit signed integer
|
|
43
|
+
["int8", "sbyte"], // 8-bit signed integer
|
|
44
|
+
["safeint", "int"], // Safe integer, use int as default
|
|
45
|
+
["uint64", "ulong"], // 64-bit unsigned integer
|
|
46
|
+
["uint32", "uint"], // 32-bit unsigned integer
|
|
47
|
+
["uint16", "ushort"], // 16-bit unsigned integer
|
|
48
|
+
["uint8", "byte"], // 8-bit unsigned integer
|
|
49
|
+
["float32", "float"], // 32-bit floating point
|
|
50
|
+
["float64", "double"], // 64-bit floating point
|
|
51
|
+
|
|
52
|
+
// Date and time types
|
|
53
|
+
["plainDate", "DateOnly"], // Use .NET 6+ DateOnly for plain calendar dates
|
|
54
|
+
["plainTime", "TimeOnly"], // Use .NET 6+ TimeOnly for plain clock times
|
|
55
|
+
["utcDateTime", "DateTimeOffset"], // Use DateTimeOffset for UTC date-times
|
|
56
|
+
["offsetDateTime", "DateTimeOffset"], // Use DateTimeOffset for timezone-specific date-times
|
|
57
|
+
["duration", "TimeSpan"], // Duration as TimeSpan
|
|
58
|
+
|
|
59
|
+
// String types
|
|
60
|
+
["url", "Uri"], // Matches C#'s `Uri`
|
|
61
|
+
]);
|
|
62
|
+
|
|
63
|
+
export function getScalarIntrinsicExpression(
|
|
64
|
+
$: Typekit,
|
|
65
|
+
type: Scalar | IntrinsicType,
|
|
66
|
+
): string | null {
|
|
67
|
+
let intrinsicName: string;
|
|
68
|
+
|
|
69
|
+
if ($.scalar.isUtcDateTime(type) || $.scalar.extendsUtcDateTime(type)) {
|
|
70
|
+
return "DateTimeOffset";
|
|
71
|
+
}
|
|
72
|
+
if ($.scalar.is(type)) {
|
|
73
|
+
intrinsicName = $.scalar.getStdBase(type)?.name ?? "";
|
|
74
|
+
} else {
|
|
75
|
+
intrinsicName = type.name;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const csType = intrinsicNameToCSharpType.get(intrinsicName);
|
|
79
|
+
|
|
80
|
+
if (!csType) {
|
|
81
|
+
reportTypescriptDiagnostic($.program, { code: "typescript-unsupported-scalar", target: type });
|
|
82
|
+
return "object"; // Fallback to object if unsupported
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return csType;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function isDeclaration($: Typekit, type: Type): boolean {
|
|
89
|
+
switch (type.kind) {
|
|
90
|
+
case "Namespace":
|
|
91
|
+
case "Interface":
|
|
92
|
+
case "Enum":
|
|
93
|
+
case "Operation":
|
|
94
|
+
case "EnumMember":
|
|
95
|
+
return true;
|
|
96
|
+
case "UnionVariant":
|
|
97
|
+
return false;
|
|
98
|
+
|
|
99
|
+
case "Model":
|
|
100
|
+
if ($.array.is(type) || $.record.is(type)) {
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return Boolean(type.name);
|
|
105
|
+
case "Union":
|
|
106
|
+
return Boolean(type.name);
|
|
107
|
+
default:
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export { intrinsicNameToCSharpType };
|