@typespec/emitter-framework 0.9.0-dev.3 → 0.9.0-dev.6

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 (67) hide show
  1. package/dist/src/csharp/components/class/declaration.d.ts +13 -0
  2. package/dist/src/csharp/components/class/declaration.d.ts.map +1 -0
  3. package/dist/src/csharp/components/{class-declaration.js → class/declaration.js} +35 -41
  4. package/dist/src/csharp/components/class/declaration.test.d.ts +2 -0
  5. package/dist/src/csharp/components/class/declaration.test.d.ts.map +1 -0
  6. package/dist/src/csharp/components/class/declaration.test.js +392 -0
  7. package/dist/src/csharp/components/enum-declaration.d.ts +1 -1
  8. package/dist/src/csharp/components/enum-declaration.d.ts.map +1 -1
  9. package/dist/src/csharp/components/enum-declaration.js +12 -9
  10. package/dist/src/csharp/components/index.d.ts +2 -1
  11. package/dist/src/csharp/components/index.d.ts.map +1 -1
  12. package/dist/src/csharp/components/index.js +2 -1
  13. package/dist/src/csharp/components/property/property.d.ts +15 -0
  14. package/dist/src/csharp/components/property/property.d.ts.map +1 -0
  15. package/dist/src/csharp/components/property/property.js +85 -0
  16. package/dist/src/csharp/components/property/property.test.d.ts +2 -0
  17. package/dist/src/csharp/components/property/property.test.d.ts.map +1 -0
  18. package/dist/src/csharp/components/property/property.test.js +117 -0
  19. package/dist/src/csharp/components/type-expression.d.ts.map +1 -1
  20. package/dist/src/csharp/components/type-expression.js +15 -1
  21. package/dist/src/csharp/components/type-expression.test.d.ts +2 -0
  22. package/dist/src/csharp/components/type-expression.test.d.ts.map +1 -0
  23. package/dist/src/csharp/components/type-expression.test.js +128 -0
  24. package/dist/src/csharp/components/utils/doc-comments.d.ts +14 -0
  25. package/dist/src/csharp/components/utils/doc-comments.d.ts.map +1 -0
  26. package/dist/src/csharp/components/utils/doc-comments.js +67 -0
  27. package/dist/src/typescript/components/arrow-function.d.ts +1 -1
  28. package/dist/src/typescript/components/arrow-function.d.ts.map +1 -1
  29. package/dist/src/typescript/components/arrow-function.js +2 -1
  30. package/dist/src/typescript/components/function-expression.d.ts +1 -1
  31. package/dist/src/typescript/components/function-expression.d.ts.map +1 -1
  32. package/dist/src/typescript/components/function-expression.js +2 -1
  33. package/dist/src/typescript/components/function-type.d.ts +1 -1
  34. package/dist/src/typescript/components/function-type.d.ts.map +1 -1
  35. package/dist/src/typescript/components/function-type.js +2 -1
  36. package/dist/test/csharp/components/enum-declaration.test.js +57 -9
  37. package/dist/test/test-host.d.ts +2 -0
  38. package/dist/test/test-host.d.ts.map +1 -0
  39. package/dist/test/test-host.js +5 -0
  40. package/dist/test/vitest.setup.d.ts +2 -0
  41. package/dist/test/vitest.setup.d.ts.map +1 -0
  42. package/dist/test/vitest.setup.js +1 -0
  43. package/package.json +10 -7
  44. package/src/csharp/components/class/declaration.test.tsx +401 -0
  45. package/src/csharp/components/class/declaration.tsx +86 -0
  46. package/src/csharp/components/enum-declaration.tsx +23 -15
  47. package/src/csharp/components/index.ts +2 -1
  48. package/src/csharp/components/property/property.test.tsx +97 -0
  49. package/src/csharp/components/property/property.tsx +62 -0
  50. package/src/csharp/components/type-expression.test.tsx +133 -0
  51. package/src/csharp/components/type-expression.tsx +10 -3
  52. package/src/csharp/components/utils/doc-comments.tsx +58 -0
  53. package/src/typescript/components/arrow-function.tsx +1 -1
  54. package/src/typescript/components/function-expression.tsx +1 -1
  55. package/src/typescript/components/function-type.tsx +1 -1
  56. package/test/csharp/components/enum-declaration.test.tsx +50 -9
  57. package/test/test-host.ts +4 -0
  58. package/test/vitest.setup.ts +1 -0
  59. package/tsconfig.json +2 -1
  60. package/vitest.config.ts +2 -1
  61. package/dist/src/csharp/components/class-declaration.d.ts +0 -9
  62. package/dist/src/csharp/components/class-declaration.d.ts.map +0 -1
  63. package/dist/test/csharp/components/class-declaration.test.d.ts +0 -2
  64. package/dist/test/csharp/components/class-declaration.test.d.ts.map +0 -1
  65. package/dist/test/csharp/components/class-declaration.test.js +0 -421
  66. package/src/csharp/components/class-declaration.tsx +0 -87
  67. package/test/csharp/components/class-declaration.test.tsx +0 -344
@@ -0,0 +1,62 @@
1
+ import { Children } from "@alloy-js/core";
2
+ import * as cs from "@alloy-js/csharp";
3
+ import { Attribute } from "@alloy-js/csharp";
4
+ import { ModelProperty, resolveEncodedName, Type } from "@typespec/compiler";
5
+ import { useTsp } from "../../../core/index.js";
6
+ import { TypeExpression } from "../type-expression.jsx";
7
+ import { getDocComments } from "../utils/doc-comments.jsx";
8
+
9
+ export interface PropertyProps {
10
+ type: ModelProperty;
11
+ /** If set the property will add the json serialization attributes(using System.Text.Json). */
12
+ jsonAttributes?: boolean;
13
+ }
14
+
15
+ /**
16
+ * Create a C# property declaration from a TypeSpec property type.
17
+ */
18
+ export function Property(props: PropertyProps): Children {
19
+ const result = preprocessPropertyType(props.type.type);
20
+ const { $ } = useTsp();
21
+
22
+ return (
23
+ <cs.Property
24
+ name={props.type.name}
25
+ type={<TypeExpression type={result.type} />}
26
+ public
27
+ required={!props.type.optional}
28
+ nullable={result.nullable}
29
+ doc={getDocComments($, props.type)}
30
+ attributes={props.jsonAttributes ? [<JsonNameAttribute type={props.type} />] : undefined}
31
+ get
32
+ set
33
+ />
34
+ );
35
+ }
36
+
37
+ export interface JsonNameAttributeProps {
38
+ type: ModelProperty;
39
+ }
40
+
41
+ function JsonNameAttribute(props: JsonNameAttributeProps): Children {
42
+ const { program } = useTsp();
43
+ const jsonName = resolveEncodedName(program, props.type, "application/json");
44
+ return <Attribute name="System.Text.Json.JsonPropertyName" args={[JSON.stringify(jsonName)]} />;
45
+ }
46
+
47
+ function preprocessPropertyType(type: Type): { type: Type; nullable: boolean } {
48
+ const { $ } = useTsp();
49
+
50
+ if (type.kind === "Union") {
51
+ const variants = type.variants;
52
+ const nonNullVariant = [...variants.values()].find((v) => v.type !== $.intrinsic.null);
53
+ const nullVariant = [...variants.values()].find((v) => v.type !== $.intrinsic.null);
54
+ if (nonNullVariant && nullVariant && variants.size === 2) {
55
+ return { type: nonNullVariant.type, nullable: true };
56
+ } else {
57
+ return { type, nullable: false };
58
+ }
59
+ } else {
60
+ return { type, nullable: false };
61
+ }
62
+ }
@@ -0,0 +1,133 @@
1
+ import { Children, render } from "@alloy-js/core";
2
+ import { d } from "@alloy-js/core/testing";
3
+ import { Namespace, SourceFile } from "@alloy-js/csharp";
4
+ import { Model, ModelProperty } from "@typespec/compiler";
5
+ import { BasicTestRunner } from "@typespec/compiler/testing";
6
+ import { beforeEach, describe, it } from "vitest";
7
+ import { createEmitterFrameworkTestRunner } from "../../../test/typescript/test-host.js";
8
+ import { assertFileContents } from "../../../test/utils.js";
9
+ import { Output } from "../../core/index.js";
10
+ import { ClassDeclaration } from "./class/declaration.js";
11
+ import { TypeExpression } from "./type-expression.jsx";
12
+
13
+ let runner: BasicTestRunner;
14
+
15
+ beforeEach(async () => {
16
+ runner = await createEmitterFrameworkTestRunner();
17
+ });
18
+
19
+ function Wrapper(props: { children: Children }) {
20
+ return (
21
+ <Output program={runner.program}>
22
+ <Namespace name="TestNamespace">
23
+ <SourceFile path="test.ts">{props.children}</SourceFile>
24
+ </Namespace>
25
+ </Output>
26
+ );
27
+ }
28
+
29
+ async function compileType(ref: string) {
30
+ const { test } = await runner.compile(`
31
+ model Test {
32
+ @test test: ${ref};
33
+ }
34
+ `);
35
+
36
+ return (test as ModelProperty).type;
37
+ }
38
+
39
+ describe("map scalar to c# built-in types", () => {
40
+ it.each([
41
+ ["string", "string"],
42
+ ["int32", "int"],
43
+ ["int64", "long"],
44
+ ])("%s => %s", async (tspType, csType) => {
45
+ const type = await compileType(tspType);
46
+ const res = render(
47
+ <Wrapper>
48
+ <TypeExpression type={type} />
49
+ </Wrapper>,
50
+ );
51
+
52
+ assertFileContents(
53
+ res,
54
+ d`
55
+ namespace TestNamespace
56
+ {
57
+ ${csType}
58
+ }
59
+ `,
60
+ );
61
+ });
62
+ });
63
+
64
+ it("maps array to c# array", async () => {
65
+ const type = await compileType("int32[]");
66
+ const res = render(
67
+ <Wrapper>
68
+ <TypeExpression type={type} />
69
+ </Wrapper>,
70
+ );
71
+
72
+ assertFileContents(
73
+ res,
74
+ d`
75
+ namespace TestNamespace
76
+ {
77
+ int[]
78
+ }
79
+ `,
80
+ );
81
+ });
82
+
83
+ describe("Record map to IDictionary", () => {
84
+ it("for primitive types", async () => {
85
+ const type = await compileType("Record<int32>");
86
+ const res = render(
87
+ <Wrapper>
88
+ <TypeExpression type={type} />
89
+ </Wrapper>,
90
+ );
91
+
92
+ assertFileContents(
93
+ res,
94
+ d`
95
+ namespace TestNamespace
96
+ {
97
+ IDictionary<string, int>
98
+ }
99
+ `,
100
+ );
101
+ });
102
+
103
+ it("for models", async () => {
104
+ const { test, Pet } = (await runner.compile(`
105
+ model Test {
106
+ @test test: Record<Pet>;
107
+ }
108
+ @test model Pet {}
109
+ `)) as { test: ModelProperty; Pet: Model };
110
+
111
+ const res = render(
112
+ <Wrapper>
113
+ <ClassDeclaration type={Pet} />
114
+ <hbr />
115
+ <TypeExpression type={test.type} />
116
+ </Wrapper>,
117
+ );
118
+
119
+ assertFileContents(
120
+ res,
121
+ d`
122
+ namespace TestNamespace
123
+ {
124
+ class Pet
125
+ {
126
+
127
+ }
128
+ IDictionary<string, Pet>
129
+ }
130
+ `,
131
+ );
132
+ });
133
+ });
@@ -1,6 +1,6 @@
1
- import { Children } from "@alloy-js/core";
1
+ import { Children, code } from "@alloy-js/core";
2
2
  import { Reference } from "@alloy-js/csharp";
3
- import { IntrinsicType, Scalar, Type } from "@typespec/compiler";
3
+ import { getTypeName, IntrinsicType, Scalar, Type } from "@typespec/compiler";
4
4
  import { Typekit } from "@typespec/compiler/typekit";
5
5
  import { useTsp } from "../../core/index.js";
6
6
  import { reportTypescriptDiagnostic } from "../../typescript/lib.js";
@@ -17,8 +17,15 @@ export function TypeExpression(props: TypeExpressionProps): Children {
17
17
  }
18
18
  if ($.scalar.is(props.type)) {
19
19
  return getScalarIntrinsicExpression($, props.type);
20
+ } else if ($.array.is(props.type)) {
21
+ return code`${(<TypeExpression type={props.type.indexer.value} />)}[]`;
22
+ } else if ($.record.is(props.type)) {
23
+ return code`IDictionary<string, ${(<TypeExpression type={props.type.indexer.value} />)}>`;
20
24
  }
21
- throw new Error("not implemented");
25
+
26
+ throw new Error(
27
+ `Unsupported type for TypeExpression: ${props.type.kind} (${getTypeName(props.type)})`,
28
+ );
22
29
  }
23
30
 
24
31
  const intrinsicNameToCSharpType = new Map<string, string | null>([
@@ -0,0 +1,58 @@
1
+ import * as ay from "@alloy-js/core";
2
+ import * as cs from "@alloy-js/csharp";
3
+ import { getReturnsDoc, Type } from "@typespec/compiler";
4
+ import { Typekit } from "@typespec/compiler/typekit";
5
+
6
+ /**
7
+ * Helper to render a doc string for a given TypeSpec type.
8
+ *
9
+ * This is not a JSX component as it needs to return undefined if there is no doc.
10
+ *
11
+ * @param $ The Typekit instance
12
+ * @param type The TypeSpec type to generate documentation for
13
+ * @returns A DocSummary component containing the rendered doc string, or undefined if no doc is available.
14
+ */
15
+ export function getDocComments($: Typekit, type: Type): ay.Children {
16
+ const typeDoc = $.type.getDoc(type);
17
+ if (!typeDoc) {
18
+ return undefined;
19
+ }
20
+
21
+ const docElements: ay.Children[] = [];
22
+
23
+ // Add main type documentation
24
+ docElements.push(
25
+ <cs.DocSummary>
26
+ <cs.DocFromMarkdown markdown={typeDoc} />
27
+ </cs.DocSummary>,
28
+ );
29
+
30
+ // Add operation-specific documentation if applicable
31
+ if ($.operation.is(type)) {
32
+ // Add parameter documentation
33
+ const paramDocs = [];
34
+ for (const param of type.parameters.properties.values()) {
35
+ const paramDoc = $.type.getDoc(param);
36
+ if (paramDoc) {
37
+ paramDocs.push(
38
+ <cs.DocParam name={param.name}>
39
+ <cs.DocFromMarkdown markdown={paramDoc} />
40
+ </cs.DocParam>,
41
+ );
42
+ }
43
+ }
44
+ docElements.push(...paramDocs);
45
+
46
+ // Add return documentation
47
+ const returnDoc = getReturnsDoc($.program, type);
48
+ if (returnDoc) {
49
+ docElements.push(
50
+ <cs.DocReturns>
51
+ <cs.DocFromMarkdown markdown={returnDoc} />
52
+ </cs.DocReturns>,
53
+ );
54
+ }
55
+ }
56
+
57
+ return <ay.List doubleHardline>{docElements}</ay.List>;
58
+ }
@@ -1,4 +1,4 @@
1
- import { splitProps } from "@alloy-js/core/jsx-runtime";
1
+ import { splitProps } from "@alloy-js/core";
2
2
  import * as ts from "@alloy-js/typescript";
3
3
  import { Operation } from "@typespec/compiler";
4
4
  import { buildParameterDescriptors, getReturnType } from "../utils/operation.js";
@@ -1,4 +1,4 @@
1
- import { splitProps } from "@alloy-js/core/jsx-runtime";
1
+ import { splitProps } from "@alloy-js/core";
2
2
  import * as ts from "@alloy-js/typescript";
3
3
  import { Operation } from "@typespec/compiler";
4
4
  import { buildParameterDescriptors, getReturnType } from "../utils/operation.js";
@@ -1,4 +1,4 @@
1
- import { splitProps } from "@alloy-js/core/jsx-runtime";
1
+ import { splitProps } from "@alloy-js/core";
2
2
  import * as ts from "@alloy-js/typescript";
3
3
  import { Operation } from "@typespec/compiler";
4
4
  import { buildParameterDescriptors, getReturnType } from "../utils/operation.js";
@@ -40,7 +40,7 @@ it("renders a basic enum declaration", async () => {
40
40
  d`
41
41
  namespace TestNamespace
42
42
  {
43
- public enum TestEnum
43
+ enum TestEnum
44
44
  {
45
45
  Value1,
46
46
  Value2,
@@ -71,7 +71,7 @@ it("renders an empty enum declaration", async () => {
71
71
  d`
72
72
  namespace TestNamespace
73
73
  {
74
- public enum TestEnum
74
+ enum TestEnum
75
75
  {
76
76
 
77
77
  }
@@ -103,7 +103,7 @@ it("can override enum name", async () => {
103
103
  d`
104
104
  namespace TestNamespace
105
105
  {
106
- public enum CustomEnumName
106
+ enum CustomEnumName
107
107
  {
108
108
  Value1,
109
109
  Value2
@@ -125,7 +125,7 @@ it("renders an enum with access modifiers", async () => {
125
125
  <Output program={runner.program}>
126
126
  <Namespace name="TestNamespace">
127
127
  <SourceFile path="test.cs">
128
- <EnumDeclaration type={TestEnum} accessModifier="internal" />
128
+ <EnumDeclaration type={TestEnum} internal />
129
129
  </SourceFile>
130
130
  </Namespace>
131
131
  </Output>,
@@ -170,7 +170,7 @@ it("renders enum with C# naming conventions", async () => {
170
170
  d`
171
171
  namespace TestNamespace
172
172
  {
173
- public enum TestEnum
173
+ enum TestEnum
174
174
  {
175
175
  ValueOne,
176
176
  ValueTwo,
@@ -203,7 +203,7 @@ it("renders enum with single value", async () => {
203
203
  d`
204
204
  namespace TestNamespace
205
205
  {
206
- public enum TestEnum
206
+ enum TestEnum
207
207
  {
208
208
  OnlyValue
209
209
  }
@@ -237,7 +237,7 @@ it("renders enum with numeric-like member names", async () => {
237
237
  d`
238
238
  namespace TestNamespace
239
239
  {
240
- public enum TestEnum
240
+ enum TestEnum
241
241
  {
242
242
  Value0,
243
243
  Value1,
@@ -279,12 +279,12 @@ it("renders multiple enums in the same namespace", async () => {
279
279
  d`
280
280
  namespace TestNamespace
281
281
  {
282
- public enum TestEnum1
282
+ enum TestEnum1
283
283
  {
284
284
  Value1,
285
285
  Value2
286
286
  }
287
- public enum TestEnum2
287
+ enum TestEnum2
288
288
  {
289
289
  OptionA,
290
290
  OptionB,
@@ -294,3 +294,44 @@ it("renders multiple enums in the same namespace", async () => {
294
294
  `,
295
295
  );
296
296
  });
297
+
298
+ it("renders an enum with doc comments", async () => {
299
+ const { TestEnum } = (await runner.compile(`
300
+ @test enum TestEnum {
301
+ @doc("This is value one")
302
+ Value1;
303
+ /** This is value two */
304
+ Value2;
305
+ }
306
+ `)) as { TestEnum: Enum };
307
+
308
+ const res = render(
309
+ <Output program={runner.program}>
310
+ <Namespace name="TestNamespace">
311
+ <SourceFile path="test.cs">
312
+ <EnumDeclaration type={TestEnum} />
313
+ </SourceFile>
314
+ </Namespace>
315
+ </Output>,
316
+ );
317
+
318
+ assertFileContents(
319
+ res,
320
+ d`
321
+ namespace TestNamespace
322
+ {
323
+ enum TestEnum
324
+ {
325
+ /// <summary>
326
+ /// This is value one
327
+ /// </summary>
328
+ Value1,
329
+ /// <summary>
330
+ /// This is value two
331
+ /// </summary>
332
+ Value2
333
+ }
334
+ }
335
+ `,
336
+ );
337
+ });
@@ -0,0 +1,4 @@
1
+ import { resolvePath } from "@typespec/compiler";
2
+ import { createTester } from "@typespec/compiler/testing";
3
+
4
+ export const Tester = createTester(resolvePath(import.meta.dirname, ".."), { libraries: [] });
@@ -0,0 +1 @@
1
+ import "@alloy-js/core/testing";
package/tsconfig.json CHANGED
@@ -14,7 +14,8 @@
14
14
  "jsxImportSource": "@alloy-js/core",
15
15
  "emitDeclarationOnly": true,
16
16
  "outDir": "dist",
17
- "rootDir": "./"
17
+ "rootDir": "./",
18
+ "types": ["@alloy-js/core/testing/matchers"]
18
19
  },
19
20
  "include": ["src/**/*.ts", "src/**/*.tsx", "test/**/*.ts", "test/**/*.tsx"],
20
21
  "exclude": ["node_modules", "dist"]
package/vitest.config.ts CHANGED
@@ -6,8 +6,9 @@ export default mergeConfig(
6
6
  defaultTypeSpecVitestConfig,
7
7
  defineConfig({
8
8
  test: {
9
- include: ["test/**/*.test.ts", "test/**/*.test.tsx"],
9
+ include: ["src/**/*.test.ts", "src/**/*.test.tsx", "test/**/*.test.ts", "test/**/*.test.tsx"],
10
10
  passWithNoTests: true,
11
+ setupFiles: ["./test/vitest.setup.ts"],
11
12
  },
12
13
  esbuild: {
13
14
  jsx: "preserve",
@@ -1,9 +0,0 @@
1
- import * as ay from "@alloy-js/core";
2
- import * as cs from "@alloy-js/csharp";
3
- import { Interface, Model } from "@typespec/compiler";
4
- export interface ClassDeclarationProps extends Omit<cs.ClassProps, "name"> {
5
- name?: string;
6
- type: Model | Interface;
7
- }
8
- export declare function ClassDeclaration(props: ClassDeclarationProps): ay.Children;
9
- //# sourceMappingURL=class-declaration.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"class-declaration.d.ts","sourceRoot":"","sources":["../../../../src/csharp/components/class-declaration.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,gBAAgB,CAAC;AACrC,OAAO,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACvC,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAKtD,MAAM,WAAW,qBAAsB,SAAQ,IAAI,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,CAAC;IACxE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,KAAK,GAAG,SAAS,CAAC;CACzB;AAUD,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,qBAAqB,GAAG,EAAE,CAAC,QAAQ,CAgB1E"}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=class-declaration.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"class-declaration.test.d.ts","sourceRoot":"","sources":["../../../../test/csharp/components/class-declaration.test.tsx"],"names":[],"mappings":""}