@alloy-js/csharp 0.17.0 → 0.18.0-dev.11

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 (117) hide show
  1. package/dist/src/components/ClassDeclaration.d.ts +57 -0
  2. package/dist/src/components/ClassDeclaration.d.ts.map +1 -0
  3. package/dist/src/components/{Class.js → ClassDeclaration.js} +42 -53
  4. package/dist/src/components/ClassMethod.d.ts +25 -0
  5. package/dist/src/components/ClassMethod.d.ts.map +1 -0
  6. package/dist/src/components/ClassMethod.js +60 -0
  7. package/dist/src/components/EnumDeclaration.d.ts +34 -0
  8. package/dist/src/components/EnumDeclaration.d.ts.map +1 -0
  9. package/dist/src/components/{Enum.js → EnumDeclaration.js} +25 -5
  10. package/dist/src/components/class/property.d.ts +55 -0
  11. package/dist/src/components/class/property.d.ts.map +1 -0
  12. package/dist/src/components/class/property.js +67 -0
  13. package/dist/src/components/class/property.test.d.ts +2 -0
  14. package/dist/src/components/class/property.test.d.ts.map +1 -0
  15. package/dist/src/components/class/property.test.js +201 -0
  16. package/dist/src/components/doc/comment.d.ts +70 -0
  17. package/dist/src/components/doc/comment.d.ts.map +1 -0
  18. package/dist/src/components/doc/comment.js +88 -0
  19. package/dist/src/components/doc/comment.test.d.ts +2 -0
  20. package/dist/src/components/doc/comment.test.d.ts.map +1 -0
  21. package/dist/src/components/doc/comment.test.js +348 -0
  22. package/dist/src/components/doc/from-markdown.d.ts +6 -0
  23. package/dist/src/components/doc/from-markdown.d.ts.map +1 -0
  24. package/dist/src/components/doc/from-markdown.js +58 -0
  25. package/dist/src/components/doc/from-markdown.test.d.ts +2 -0
  26. package/dist/src/components/doc/from-markdown.test.d.ts.map +1 -0
  27. package/dist/src/components/doc/from-markdown.test.js +83 -0
  28. package/dist/src/components/index.d.ts +9 -2
  29. package/dist/src/components/index.d.ts.map +1 -1
  30. package/dist/src/components/index.js +9 -2
  31. package/dist/src/components/interface/declaration.d.ts +34 -0
  32. package/dist/src/components/interface/declaration.d.ts.map +1 -0
  33. package/dist/src/components/interface/declaration.js +90 -0
  34. package/dist/src/components/interface/declaration.test.d.ts +2 -0
  35. package/dist/src/components/interface/declaration.test.d.ts.map +1 -0
  36. package/dist/src/components/interface/declaration.test.js +69 -0
  37. package/dist/src/components/interface/method.d.ts +18 -0
  38. package/dist/src/components/interface/method.d.ts.map +1 -0
  39. package/dist/src/components/interface/method.js +59 -0
  40. package/dist/src/components/interface/method.test.d.ts +2 -0
  41. package/dist/src/components/interface/method.test.d.ts.map +1 -0
  42. package/dist/src/components/interface/method.test.js +131 -0
  43. package/dist/src/components/interface/property.d.ts +38 -0
  44. package/dist/src/components/interface/property.d.ts.map +1 -0
  45. package/dist/src/components/interface/property.js +67 -0
  46. package/dist/src/components/interface/property.test.d.ts +2 -0
  47. package/dist/src/components/interface/property.test.d.ts.map +1 -0
  48. package/dist/src/components/interface/property.test.js +165 -0
  49. package/dist/src/components/stc/index.d.ts +2 -2
  50. package/dist/src/components/stc/index.d.ts.map +1 -1
  51. package/dist/src/components/stc/index.js +2 -2
  52. package/dist/src/modifiers.d.ts +13 -4
  53. package/dist/src/modifiers.d.ts.map +1 -1
  54. package/dist/src/modifiers.js +13 -27
  55. package/dist/src/name-policy.d.ts +1 -1
  56. package/dist/src/name-policy.d.ts.map +1 -1
  57. package/dist/src/name-policy.js +1 -0
  58. package/dist/test/class-declaration.test.d.ts +2 -0
  59. package/dist/test/class-declaration.test.d.ts.map +1 -0
  60. package/dist/test/{class.test.js → class-declaration.test.js} +123 -73
  61. package/dist/test/class-method.test.d.ts +2 -0
  62. package/dist/test/class-method.test.d.ts.map +1 -0
  63. package/dist/test/class-method.test.js +161 -0
  64. package/dist/test/enum.test.js +12 -12
  65. package/dist/test/namespace.test.js +8 -8
  66. package/dist/test/project-directory.test.d.ts +2 -0
  67. package/dist/test/project-directory.test.d.ts.map +1 -0
  68. package/dist/test/{projectdirectory.test.js → project-directory.test.js} +8 -8
  69. package/dist/test/sourcefile.test.js +4 -4
  70. package/dist/test/using.test.js +9 -9
  71. package/dist/test/utils.d.ts +3 -0
  72. package/dist/test/utils.d.ts.map +1 -1
  73. package/dist/test/utils.js +15 -0
  74. package/dist/test/vitest.setup.d.ts +2 -0
  75. package/dist/test/vitest.setup.d.ts.map +1 -0
  76. package/dist/test/vitest.setup.js +1 -0
  77. package/dist/tsconfig.tsbuildinfo +1 -1
  78. package/package.json +7 -6
  79. package/src/components/{Class.tsx → ClassDeclaration.tsx} +68 -66
  80. package/src/components/ClassMethod.tsx +94 -0
  81. package/src/components/{Enum.tsx → EnumDeclaration.tsx} +30 -6
  82. package/src/components/class/property.test.tsx +180 -0
  83. package/src/components/class/property.tsx +135 -0
  84. package/src/components/doc/comment.test.tsx +337 -0
  85. package/src/components/doc/comment.tsx +152 -0
  86. package/src/components/doc/from-markdown.test.tsx +103 -0
  87. package/src/components/doc/from-markdown.tsx +58 -0
  88. package/src/components/index.ts +9 -2
  89. package/src/components/interface/declaration.test.tsx +56 -0
  90. package/src/components/interface/declaration.tsx +109 -0
  91. package/src/components/interface/method.test.tsx +120 -0
  92. package/src/components/interface/method.tsx +82 -0
  93. package/src/components/interface/property.test.tsx +144 -0
  94. package/src/components/interface/property.tsx +107 -0
  95. package/src/components/stc/index.ts +2 -2
  96. package/src/modifiers.ts +32 -37
  97. package/src/name-policy.ts +2 -0
  98. package/temp/api.json +4419 -567
  99. package/test/{class.test.tsx → class-declaration.test.tsx} +103 -99
  100. package/test/class-method.test.tsx +147 -0
  101. package/test/enum.test.tsx +11 -11
  102. package/test/namespace.test.tsx +4 -4
  103. package/test/{projectdirectory.test.tsx → project-directory.test.tsx} +4 -4
  104. package/test/sourcefile.test.tsx +2 -2
  105. package/test/using.test.tsx +9 -9
  106. package/test/utils.tsx +9 -0
  107. package/test/vitest.setup.ts +1 -0
  108. package/tsconfig.json +2 -1
  109. package/vitest.config.ts +3 -0
  110. package/dist/src/components/Class.d.ts +0 -36
  111. package/dist/src/components/Class.d.ts.map +0 -1
  112. package/dist/src/components/Enum.d.ts +0 -15
  113. package/dist/src/components/Enum.d.ts.map +0 -1
  114. package/dist/test/class.test.d.ts +0 -2
  115. package/dist/test/class.test.d.ts.map +0 -1
  116. package/dist/test/projectdirectory.test.d.ts +0 -2
  117. package/dist/test/projectdirectory.test.d.ts.map +0 -1
@@ -1,26 +1,68 @@
1
1
  import * as core from "@alloy-js/core";
2
2
  import {
3
- AccessModifier,
3
+ AccessModifiers,
4
+ computeModifiersPrefix,
4
5
  getAccessModifier,
5
- getMethodModifier,
6
- MethodModifier,
6
+ makeModifiers,
7
7
  } from "../modifiers.js";
8
8
  import { CSharpElements, useCSharpNamePolicy } from "../name-policy.js";
9
9
  import { CSharpOutputSymbol } from "../symbols/csharp-output-symbol.js";
10
10
  import { CSharpMemberScope, useCSharpScope } from "../symbols/scopes.js";
11
- import { Name } from "./Name.js";
12
- import { ParameterProps, Parameters } from "./Parameters.js";
11
+ import { Name } from "./Name.jsx";
12
+ import { ParameterProps, Parameters } from "./Parameters.jsx";
13
+ import { DocWhen } from "./doc/comment.jsx";
14
+
15
+ export interface ClassModifiers {
16
+ readonly abstract?: boolean;
17
+ readonly partial?: boolean;
18
+ readonly sealed?: boolean;
19
+ readonly static?: boolean;
20
+ }
21
+
22
+ const getClassModifiers = makeModifiers<ClassModifiers>([
23
+ "abstract",
24
+ "partial",
25
+ "sealed",
26
+ "static",
27
+ ]);
13
28
 
14
29
  // properties for creating a class
15
- export interface ClassProps extends Omit<core.DeclarationProps, "nameKind"> {
30
+ export interface ClassDeclarationProps
31
+ extends Omit<core.DeclarationProps, "nameKind">,
32
+ AccessModifiers,
33
+ ClassModifiers {
16
34
  name: string;
35
+ /** Doc comment */
36
+ doc?: core.Children;
17
37
  refkey?: core.Refkey;
18
- accessModifier?: AccessModifier;
19
38
  typeParameters?: Record<string, core.Refkey>;
20
39
  }
21
40
 
22
- // a C# class declaration
23
- export function Class(props: ClassProps) {
41
+ /**
42
+ * CSharp class declaration.
43
+ * @example
44
+ * ```tsx
45
+ * <ClassDeclaration public name="MyClass">
46
+ * <ClassMember public name="MyField" type="int" />
47
+ * <ClassConstructor>
48
+ * <Parameter name="value" type="int" />
49
+ * this.MyField = value;
50
+ * </ClassConstructor>
51
+ * </ClassDeclaration>
52
+ * ```
53
+ * This will produce:
54
+ * ```csharp
55
+ * public class MyClass
56
+ * {
57
+ * public int MyField;
58
+ * public MyClass(int value)
59
+ * {
60
+ * this.MyField = value;
61
+ * }
62
+ * }
63
+ * ```
64
+ */
65
+ export function ClassDeclaration(props: ClassDeclarationProps) {
24
66
  const name = useCSharpNamePolicy().getName(props.name!, "class");
25
67
 
26
68
  const thisClassSymbol = new CSharpOutputSymbol(name, {
@@ -60,9 +102,14 @@ export function Class(props: ClassProps) {
60
102
  );
61
103
  }
62
104
 
105
+ const modifiers = computeModifiersPrefix([
106
+ getAccessModifier(props),
107
+ getClassModifiers(props),
108
+ ]);
63
109
  return (
64
110
  <core.Declaration symbol={thisClassSymbol}>
65
- {getAccessModifier(props.accessModifier)}class <Name />
111
+ <DocWhen doc={props.doc} />
112
+ {modifiers}class <Name />
66
113
  {typeParams}
67
114
  {!props.children && ";"}
68
115
  {props.children && (
@@ -74,8 +121,7 @@ export function Class(props: ClassProps) {
74
121
  );
75
122
  }
76
123
 
77
- export interface ClassConstructorProps {
78
- accessModifier?: AccessModifier;
124
+ export interface ClassConstructorProps extends AccessModifiers {
79
125
  parameters?: Array<ParameterProps>;
80
126
  refkey?: core.Refkey;
81
127
  symbol?: core.OutputSymbol;
@@ -103,7 +149,8 @@ export function ClassConstructor(props: ClassConstructorProps) {
103
149
  owner: ctorSymbol,
104
150
  });
105
151
 
106
- const accessModifier = getAccessModifier(props.accessModifier);
152
+ const modifiers = computeModifiersPrefix([getAccessModifier(props)]);
153
+
107
154
  const params =
108
155
  props.parameters ? <Parameters parameters={props.parameters} /> : "";
109
156
 
@@ -111,7 +158,7 @@ export function ClassConstructor(props: ClassConstructorProps) {
111
158
  return (
112
159
  <core.Declaration symbol={ctorSymbol}>
113
160
  <core.Scope value={ctorDeclScope}>
114
- {accessModifier}
161
+ {modifiers}
115
162
  <Name />({params})<core.Block newline>{props.children}</core.Block>
116
163
  </core.Scope>
117
164
  </core.Declaration>
@@ -119,17 +166,18 @@ export function ClassConstructor(props: ClassConstructorProps) {
119
166
  }
120
167
 
121
168
  // properties for creating a class member
122
- export interface ClassMemberProps {
169
+ export interface ClassMemberProps extends AccessModifiers {
123
170
  name: string;
124
171
  type: core.Children;
125
- accessModifier?: AccessModifier;
126
172
  refkey?: core.Refkey;
173
+ /** Doc comment */
174
+ doc?: core.Children;
127
175
  }
128
176
 
129
177
  // a C# class member (i.e. a field within a class like "private int count")
130
178
  export function ClassMember(props: ClassMemberProps) {
131
179
  let nameElement: CSharpElements = "class-member-private";
132
- if (props.accessModifier === "public") {
180
+ if (props.public) {
133
181
  nameElement = "class-member-public";
134
182
  }
135
183
  const name = useCSharpNamePolicy().getName(props.name, nameElement);
@@ -145,58 +193,12 @@ export function ClassMember(props: ClassMemberProps) {
145
193
  refkeys: props.refkey ?? core.refkey(props.name),
146
194
  });
147
195
 
196
+ const modifiers = computeModifiersPrefix([getAccessModifier(props)]);
148
197
  return (
149
198
  <core.Declaration symbol={memberSymbol}>
150
- {getAccessModifier(props.accessModifier)}
199
+ <DocWhen doc={props.doc} />
200
+ {modifiers}
151
201
  {props.type} <Name />
152
202
  </core.Declaration>
153
203
  );
154
204
  }
155
-
156
- // properties for creating a method
157
- export interface ClassMethodProps {
158
- name: string;
159
- refkey?: core.Refkey;
160
- children?: core.Children;
161
- accessModifier?: AccessModifier;
162
- methodModifier?: MethodModifier;
163
- parameters?: Array<ParameterProps>;
164
- returns?: core.Children;
165
- }
166
-
167
- // a C# class method
168
- export function ClassMethod(props: ClassMethodProps) {
169
- const name = useCSharpNamePolicy().getName(props.name!, "class-method");
170
- const scope = useCSharpScope();
171
- if (scope.kind !== "member" || scope.name !== "class-decl") {
172
- throw new Error("can't define a class method outside of a class scope");
173
- }
174
-
175
- const methodSymbol = new CSharpOutputSymbol(name, {
176
- scope,
177
- refkeys: props.refkey ?? core.refkey(props.name),
178
- });
179
-
180
- // scope for method declaration
181
- const methodScope = new CSharpMemberScope("method-decl", {
182
- owner: methodSymbol,
183
- });
184
-
185
- const accessModifier = getAccessModifier(props.accessModifier);
186
- const methodModifier = getMethodModifier(props.methodModifier);
187
- const params =
188
- props.parameters ? <Parameters parameters={props.parameters} /> : "";
189
- const returns = props.returns ?? "void";
190
-
191
- // note that scope wraps the method decl so that the params get the correct scope
192
- return (
193
- <core.Declaration symbol={methodSymbol}>
194
- <core.Scope value={methodScope}>
195
- {accessModifier}
196
- {methodModifier}
197
- {returns} <Name />({params})
198
- <core.Block newline>{props.children}</core.Block>
199
- </core.Scope>
200
- </core.Declaration>
201
- );
202
- }
@@ -0,0 +1,94 @@
1
+ import {
2
+ Block,
3
+ Children,
4
+ MemberDeclaration,
5
+ refkey,
6
+ Refkey,
7
+ Scope,
8
+ } from "@alloy-js/core";
9
+ import {
10
+ AccessModifiers,
11
+ computeModifiersPrefix,
12
+ getAccessModifier,
13
+ getAsyncModifier,
14
+ makeModifiers,
15
+ } from "../modifiers.js";
16
+ import { useCSharpNamePolicy } from "../name-policy.js";
17
+ import { CSharpOutputSymbol } from "../symbols/csharp-output-symbol.js";
18
+ import { CSharpMemberScope, useCSharpScope } from "../symbols/scopes.js";
19
+ import { ParameterProps, Parameters } from "./Parameters.jsx";
20
+ import { DocWhen } from "./doc/comment.jsx";
21
+
22
+ /** Method modifiers. Can only be one. */
23
+ export interface ClassMethodModifiers {
24
+ readonly abstract?: boolean;
25
+ readonly sealed?: boolean;
26
+ readonly static?: boolean;
27
+ readonly virtual?: boolean;
28
+ }
29
+
30
+ const getMethodModifier = makeModifiers<ClassMethodModifiers>([
31
+ "abstract",
32
+ "sealed",
33
+ "static",
34
+ "virtual",
35
+ ]);
36
+
37
+ // properties for creating a method
38
+ export interface ClassMethodProps
39
+ extends AccessModifiers,
40
+ ClassMethodModifiers {
41
+ name: string;
42
+ refkey?: Refkey;
43
+ children?: Children;
44
+ parameters?: Array<ParameterProps>;
45
+ returns?: Children;
46
+
47
+ /**
48
+ * If true, the method will be declared as an async method.
49
+ */
50
+ async?: boolean;
51
+
52
+ /** Doc comment */
53
+ doc?: Children;
54
+ }
55
+
56
+ // a C# class method
57
+ export function ClassMethod(props: ClassMethodProps) {
58
+ const name = useCSharpNamePolicy().getName(props.name, "class-method");
59
+ const scope = useCSharpScope();
60
+ if (scope.kind !== "member" || scope.name !== "class-decl") {
61
+ throw new Error("can't define a class method outside of a class scope");
62
+ }
63
+
64
+ const methodSymbol = new CSharpOutputSymbol(name, {
65
+ scope,
66
+ refkeys: props.refkey ?? refkey(props.name),
67
+ });
68
+
69
+ // scope for method declaration
70
+ const methodScope = new CSharpMemberScope("method-decl", {
71
+ owner: methodSymbol,
72
+ });
73
+
74
+ const params =
75
+ props.parameters ? <Parameters parameters={props.parameters} /> : "";
76
+ const returns = props.returns ?? (props.async ? "Task" : "void");
77
+
78
+ const modifiers = computeModifiersPrefix([
79
+ getAccessModifier(props),
80
+ getMethodModifier(props),
81
+ getAsyncModifier(props.async),
82
+ ]);
83
+ // note that scope wraps the method decl so that the params get the correct scope
84
+ return (
85
+ <MemberDeclaration symbol={methodSymbol}>
86
+ <Scope value={methodScope}>
87
+ <DocWhen doc={props.doc} />
88
+ {modifiers}
89
+ {returns} {name}({params})
90
+ {props.abstract ? ";" : <Block newline>{props.children}</Block>}
91
+ </Scope>
92
+ </MemberDeclaration>
93
+ );
94
+ }
@@ -1,20 +1,42 @@
1
1
  import * as core from "@alloy-js/core";
2
- import { AccessModifier, getAccessModifier } from "../modifiers.js";
2
+ import {
3
+ AccessModifiers,
4
+ computeModifiersPrefix,
5
+ getAccessModifier,
6
+ } from "../modifiers.js";
3
7
  import { useCSharpNamePolicy } from "../name-policy.js";
4
8
  import { CSharpOutputSymbol } from "../symbols/csharp-output-symbol.js";
5
9
  import { CSharpMemberScope, useCSharpScope } from "../symbols/scopes.js";
6
10
  import { Name } from "./Name.jsx";
7
11
 
8
12
  // properties for creating an enum
9
- export interface EnumProps {
13
+ export interface EnumDeclarationProps extends AccessModifiers {
10
14
  name: string;
11
15
  refkey?: core.Refkey;
12
16
  children?: core.Children;
13
- accessModifier?: AccessModifier;
14
17
  }
15
18
 
16
- // a C# enum declaration
17
- export function Enum(props: EnumProps) {
19
+ /**
20
+ * A C# enum declaration
21
+ * @example
22
+ * ```tsx
23
+ * <EnumDeclaration public name="Color">
24
+ * <EnumMember name="Red" />
25
+ * <EnumMember name="Green" />
26
+ * <EnumMember name="Blue" />
27
+ * </EnumDeclaration>
28
+ * ```
29
+ * This will produce:
30
+ * ```csharp
31
+ * public enum Color
32
+ * {
33
+ * Red,
34
+ * Green,
35
+ * Blue
36
+ * }
37
+ * ```
38
+ */
39
+ export function EnumDeclaration(props: EnumDeclarationProps) {
18
40
  const name = useCSharpNamePolicy().getName(props.name!, "enum");
19
41
  const scope = useCSharpScope();
20
42
 
@@ -32,10 +54,12 @@ export function Enum(props: EnumProps) {
32
54
  owner: thisEnumSymbol,
33
55
  });
34
56
 
57
+ const modifiers = computeModifiersPrefix([getAccessModifier(props)]);
58
+
35
59
  if (thisEnumScope.owner)
36
60
  return (
37
61
  <core.Declaration symbol={thisEnumSymbol}>
38
- {getAccessModifier(props.accessModifier)}enum <Name />
62
+ {modifiers}enum <Name />
39
63
  {!props.children && ";"}
40
64
  {props.children && (
41
65
  <core.Scope value={thisEnumScope}>
@@ -0,0 +1,180 @@
1
+ import { Children } from "@alloy-js/core/jsx-runtime";
2
+ import { describe, expect, it } from "vitest";
3
+ import { TestNamespace } from "../../../test/utils.jsx";
4
+ import { ClassDeclaration } from "../ClassDeclaration.jsx";
5
+ import { ClassProperty } from "./property.jsx";
6
+
7
+ const Wrapper = (props: { children: Children }) => (
8
+ <TestNamespace>
9
+ <ClassDeclaration public name="TestClass">
10
+ {props.children}
11
+ </ClassDeclaration>
12
+ </TestNamespace>
13
+ );
14
+
15
+ describe("modifiers", () => {
16
+ describe("access modifiers", () => {
17
+ it.each(["public", "private", "protected", "internal"] as const)(
18
+ "%s",
19
+ (accessModifier) => {
20
+ expect(
21
+ <Wrapper>
22
+ <ClassProperty
23
+ {...{ [accessModifier]: true }}
24
+ name="TestProp"
25
+ type="string"
26
+ get
27
+ />
28
+ </Wrapper>,
29
+ ).toRenderTo(`
30
+ public class TestClass
31
+ {
32
+ ${accessModifier} string TestProp { get; }
33
+ }
34
+ `);
35
+ },
36
+ );
37
+ });
38
+
39
+ describe("property modifiers", () => {
40
+ it.each([
41
+ "new",
42
+ "static",
43
+ "virtual",
44
+ "sealed",
45
+ "override",
46
+ "abstract",
47
+ "extern",
48
+ "readonly",
49
+ ] as const)("%s", (methodModifier) => {
50
+ expect(
51
+ <Wrapper>
52
+ <ClassProperty
53
+ {...{ [methodModifier]: true }}
54
+ name="TestProp"
55
+ type="string"
56
+ get
57
+ />
58
+ </Wrapper>,
59
+ ).toRenderTo(`
60
+ public class TestClass
61
+ {
62
+ ${methodModifier} string TestProp { get; }
63
+ }
64
+ `);
65
+ });
66
+ });
67
+
68
+ it("combine modifiers", () => {
69
+ expect(
70
+ <Wrapper>
71
+ <ClassProperty public new name="TestProp" type="string" get />
72
+ </Wrapper>,
73
+ ).toRenderTo(`
74
+ public class TestClass
75
+ {
76
+ public new string TestProp { get; }
77
+ }
78
+ `);
79
+ });
80
+ });
81
+
82
+ it("applies PascalCase naming policy", () => {
83
+ expect(
84
+ <Wrapper>
85
+ <ClassProperty name="test_prop" type="string" get />
86
+ </Wrapper>,
87
+ ).toRenderTo(`
88
+ public class TestClass
89
+ {
90
+ string TestProp { get; }
91
+ }
92
+ `);
93
+ });
94
+
95
+ it("has getter only", () => {
96
+ expect(
97
+ <Wrapper>
98
+ <ClassProperty name="TestProp" type="string" get />
99
+ </Wrapper>,
100
+ ).toRenderTo(`
101
+ public class TestClass
102
+ {
103
+ string TestProp { get; }
104
+ }
105
+ `);
106
+ });
107
+
108
+ it("has setter only", () => {
109
+ expect(
110
+ <Wrapper>
111
+ <ClassProperty name="TestProp" type="string" set />
112
+ </Wrapper>,
113
+ ).toRenderTo(`
114
+ public class TestClass
115
+ {
116
+ string TestProp { set; }
117
+ }
118
+ `);
119
+ });
120
+
121
+ it("has getter and setter", () => {
122
+ expect(
123
+ <Wrapper>
124
+ <ClassProperty name="TestProp" type="string" get set />
125
+ </Wrapper>,
126
+ ).toRenderTo(`
127
+ public class TestClass
128
+ {
129
+ string TestProp { get; set; }
130
+ }
131
+ `);
132
+ });
133
+
134
+ it("specify doc comment", () => {
135
+ expect(
136
+ <TestNamespace>
137
+ <ClassDeclaration name="Test">
138
+ <ClassProperty
139
+ name="Method"
140
+ type="string"
141
+ get
142
+ set
143
+ doc="This is a test"
144
+ />
145
+ </ClassDeclaration>
146
+ </TestNamespace>,
147
+ ).toRenderTo(`
148
+ class Test
149
+ {
150
+ /// This is a test
151
+ string Method { get; set; }
152
+ }
153
+ `);
154
+ });
155
+
156
+ it("specify nullable property", () => {
157
+ expect(
158
+ <Wrapper>
159
+ <ClassProperty name="TestProp" type="string" nullable get set />
160
+ </Wrapper>,
161
+ ).toRenderTo(`
162
+ public class TestClass
163
+ {
164
+ string? TestProp { get; set; }
165
+ }
166
+ `);
167
+ });
168
+
169
+ it("specify initializer", () => {
170
+ expect(
171
+ <Wrapper>
172
+ <ClassProperty name="TestProp" type="string" get set init={`"abc"`} />
173
+ </Wrapper>,
174
+ ).toRenderTo(`
175
+ public class TestClass
176
+ {
177
+ string TestProp { get; set; } = "abc";
178
+ }
179
+ `);
180
+ });
@@ -0,0 +1,135 @@
1
+ import {
2
+ Block,
3
+ Children,
4
+ code,
5
+ List,
6
+ MemberDeclaration,
7
+ refkey,
8
+ Refkey,
9
+ Scope,
10
+ } from "@alloy-js/core";
11
+ import {
12
+ AccessModifiers,
13
+ computeModifiersPrefix,
14
+ getAccessModifier,
15
+ makeModifiers,
16
+ } from "../../modifiers.js";
17
+ import { useCSharpNamePolicy } from "../../name-policy.js";
18
+ import { CSharpOutputSymbol } from "../../symbols/csharp-output-symbol.js";
19
+ import { CSharpMemberScope, useCSharpScope } from "../../symbols/scopes.js";
20
+ import { DocWhen } from "../doc/comment.jsx";
21
+
22
+ /** Method modifiers. Can only be one. */
23
+ export interface ClassPropertyModifiers {
24
+ readonly new?: boolean;
25
+ readonly static?: boolean;
26
+ readonly virtual?: boolean;
27
+ readonly sealed?: boolean;
28
+ readonly override?: boolean;
29
+ readonly abstract?: boolean;
30
+ readonly extern?: boolean;
31
+ readonly readonly?: boolean;
32
+ }
33
+
34
+ const getModifiers = makeModifiers<ClassPropertyModifiers>([
35
+ "new",
36
+ "static",
37
+ "virtual",
38
+ "sealed",
39
+ "override",
40
+ "abstract",
41
+ "extern",
42
+ "readonly",
43
+ ]);
44
+
45
+ /** Properties for {@link ClassProperty} component */
46
+ export interface ClassPropertyProps
47
+ extends AccessModifiers,
48
+ ClassPropertyModifiers {
49
+ name: string;
50
+ refkey?: Refkey;
51
+
52
+ /** Property type */
53
+ type: Children;
54
+
55
+ /** If property should have a getter */
56
+ get?: boolean;
57
+
58
+ /** If property should have a setter */
59
+ set?: boolean;
60
+
61
+ /** Doc comment */
62
+ doc?: Children;
63
+
64
+ /**
65
+ * Property initializer
66
+ * @example `<ClassProperty name="My" get set nullable />`
67
+ *
68
+ * ```cs
69
+ * int? My { get; set; };
70
+ * ```
71
+ */
72
+ nullable?: boolean;
73
+
74
+ /**
75
+ * Property initializer
76
+ * @example `<ClassProperty name="My" get set init={42} />`
77
+ *
78
+ * ```cs
79
+ * int My { get; set; } = 42;
80
+ * ```
81
+ */
82
+ init?: Children;
83
+ }
84
+
85
+ /**
86
+ * Render a C# class property.
87
+ *
88
+ * @example `<ClassProperty public name="My" get set />`
89
+ *
90
+ * ```cs
91
+ * public int My { get; set; };
92
+ * ```
93
+ */
94
+ export function ClassProperty(props: ClassPropertyProps) {
95
+ const name = useCSharpNamePolicy().getName(props.name, "class-property");
96
+ const scope = useCSharpScope();
97
+ if (scope.kind !== "member" || scope.name !== "class-decl") {
98
+ throw new Error(
99
+ "can't define an interface method outside of an interface scope",
100
+ );
101
+ }
102
+
103
+ const propertySymbol = new CSharpOutputSymbol(name, {
104
+ scope,
105
+ refkeys: props.refkey ?? refkey(props.name),
106
+ });
107
+
108
+ // scope for property declaration
109
+ const propertyScope = new CSharpMemberScope("property-decl", {
110
+ owner: propertySymbol,
111
+ });
112
+
113
+ const modifiers = computeModifiersPrefix([
114
+ getAccessModifier(props),
115
+ getModifiers(props),
116
+ ]);
117
+ // note that scope wraps the method decl so that the params get the correct scope
118
+ return (
119
+ <MemberDeclaration symbol={propertySymbol}>
120
+ <Scope value={propertyScope}>
121
+ <DocWhen doc={props.doc} />
122
+ {modifiers}
123
+ {props.type}
124
+ {props.nullable && "?"} {name}{" "}
125
+ <Block newline inline>
126
+ <List joiner=" ">
127
+ {props.get && "get;"}
128
+ {props.set && "set;"}
129
+ </List>
130
+ </Block>
131
+ {props.init && code` = ${props.init};`}
132
+ </Scope>
133
+ </MemberDeclaration>
134
+ );
135
+ }