@typespec/emitter-framework 0.9.0-dev.1 → 0.9.0-dev.2

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.
Files changed (41) hide show
  1. package/dist/src/csharp/components/class-declaration.d.ts +9 -0
  2. package/dist/src/csharp/components/class-declaration.d.ts.map +1 -0
  3. package/dist/src/csharp/components/class-declaration.js +97 -0
  4. package/dist/src/csharp/components/enum-declaration.d.ts +9 -0
  5. package/dist/src/csharp/components/enum-declaration.d.ts.map +1 -0
  6. package/dist/src/csharp/components/enum-declaration.js +52 -0
  7. package/dist/src/csharp/components/index.d.ts +3 -0
  8. package/dist/src/csharp/components/index.d.ts.map +1 -0
  9. package/dist/src/csharp/components/index.js +2 -0
  10. package/dist/src/csharp/components/type-expression.d.ts +11 -0
  11. package/dist/src/csharp/components/type-expression.d.ts.map +1 -0
  12. package/dist/src/csharp/components/type-expression.js +129 -0
  13. package/dist/src/csharp/components/utils/refkey.d.ts +23 -0
  14. package/dist/src/csharp/components/utils/refkey.d.ts.map +1 -0
  15. package/dist/src/csharp/components/utils/refkey.js +35 -0
  16. package/dist/src/csharp/index.d.ts +2 -0
  17. package/dist/src/csharp/index.d.ts.map +1 -0
  18. package/dist/src/csharp/index.js +1 -0
  19. package/dist/test/csharp/components/class-declaration.test.d.ts +2 -0
  20. package/dist/test/csharp/components/class-declaration.test.d.ts.map +1 -0
  21. package/dist/test/csharp/components/class-declaration.test.js +421 -0
  22. package/dist/test/csharp/components/enum-declaration.test.d.ts +2 -0
  23. package/dist/test/csharp/components/enum-declaration.test.d.ts.map +1 -0
  24. package/dist/test/csharp/components/enum-declaration.test.js +355 -0
  25. package/dist/test/csharp/test-host.d.ts +11 -0
  26. package/dist/test/csharp/test-host.d.ts.map +1 -0
  27. package/dist/test/csharp/test-host.js +32 -0
  28. package/dist/test/csharp/utils.d.ts +3 -0
  29. package/dist/test/csharp/utils.d.ts.map +1 -0
  30. package/dist/test/csharp/utils.js +6 -0
  31. package/package.json +2 -1
  32. package/src/csharp/components/class-declaration.tsx +87 -0
  33. package/src/csharp/components/enum-declaration.tsx +48 -0
  34. package/src/csharp/components/index.ts +2 -0
  35. package/src/csharp/components/type-expression.tsx +112 -0
  36. package/src/csharp/components/utils/refkey.ts +36 -0
  37. package/src/csharp/index.ts +1 -0
  38. package/test/csharp/components/class-declaration.test.tsx +344 -0
  39. package/test/csharp/components/enum-declaration.test.tsx +296 -0
  40. package/test/csharp/test-host.ts +42 -0
  41. 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,3 @@
1
+ import { OutputDirectory } from "@alloy-js/core";
2
+ export declare function assertFileContents(res: OutputDirectory, contents: string): void;
3
+ //# sourceMappingURL=utils.d.ts.map
@@ -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"}
@@ -0,0 +1,6 @@
1
+ import { assert } from "vitest";
2
+ export function assertFileContents(res, contents) {
3
+ const testFile = res.contents.find(file => file.path === "test.cs");
4
+ assert(testFile, "test.cs file not rendered");
5
+ assert.equal(testFile.contents, contents);
6
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@typespec/emitter-framework",
3
- "version": "0.9.0-dev.1",
3
+ "version": "0.9.0-dev.2",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "repository": {
@@ -25,6 +25,7 @@
25
25
  "peerDependencies": {
26
26
  "@alloy-js/core": "^0.17.0",
27
27
  "@alloy-js/typescript": "^0.17.0",
28
+ "@alloy-js/csharp": "^0.17.0",
28
29
  "@typespec/compiler": "^1.1.0",
29
30
  "@typespec/http": "^1.1.0",
30
31
  "@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,2 @@
1
+ export * from "./class-declaration.jsx";
2
+ export * from "./enum-declaration.jsx";
@@ -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 };