@alloy-js/csharp 0.20.0-dev.1 → 0.20.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.
Files changed (42) hide show
  1. package/dist/src/components/EnumDeclaration.d.ts.map +1 -1
  2. package/dist/src/components/EnumDeclaration.js +2 -5
  3. package/dist/src/components/class/declaration.js +1 -1
  4. package/dist/src/components/class/declaration.test.js +22 -1
  5. package/dist/src/components/constructor/constructor.d.ts.map +1 -1
  6. package/dist/src/components/constructor/constructor.js +2 -5
  7. package/dist/src/components/field/field.d.ts.map +1 -1
  8. package/dist/src/components/field/field.js +2 -5
  9. package/dist/src/components/interface/method.d.ts.map +1 -1
  10. package/dist/src/components/interface/method.js +2 -5
  11. package/dist/src/components/interface/property.d.ts.map +1 -1
  12. package/dist/src/components/interface/property.js +2 -5
  13. package/dist/src/components/method/method.d.ts.map +1 -1
  14. package/dist/src/components/method/method.js +2 -5
  15. package/dist/src/components/parameters/parameters.d.ts.map +1 -1
  16. package/dist/src/components/parameters/parameters.js +2 -5
  17. package/dist/src/components/property/property.d.ts.map +1 -1
  18. package/dist/src/components/property/property.js +2 -5
  19. package/dist/src/components/record/declaration.d.ts +18 -0
  20. package/dist/src/components/record/declaration.d.ts.map +1 -1
  21. package/dist/src/components/record/declaration.js +11 -1
  22. package/dist/src/components/record/declaration.test.js +56 -2
  23. package/dist/src/symbols/scopes.d.ts +6 -0
  24. package/dist/src/symbols/scopes.d.ts.map +1 -1
  25. package/dist/src/symbols/scopes.js +11 -0
  26. package/dist/tsconfig.tsbuildinfo +1 -1
  27. package/package.json +5 -5
  28. package/src/components/EnumDeclaration.tsx +6 -7
  29. package/src/components/class/declaration.test.tsx +19 -1
  30. package/src/components/class/declaration.tsx +1 -1
  31. package/src/components/constructor/constructor.tsx +5 -10
  32. package/src/components/field/field.tsx +2 -10
  33. package/src/components/interface/method.tsx +6 -7
  34. package/src/components/interface/property.tsx +6 -7
  35. package/src/components/method/method.tsx +5 -10
  36. package/src/components/parameters/parameters.tsx +7 -12
  37. package/src/components/property/property.tsx +9 -12
  38. package/src/components/record/declaration.test.tsx +53 -2
  39. package/src/components/record/declaration.tsx +24 -0
  40. package/src/symbols/scopes.ts +20 -0
  41. package/temp/api.json +205 -1
  42. package/tsdoc-metadata.json +1 -1
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@alloy-js/csharp",
3
- "version": "0.20.0-dev.1",
4
- "description": "",
3
+ "version": "0.20.0-dev.3",
4
+ "description": "Alloy components for CSharp language.",
5
5
  "exports": {
6
6
  ".": {
7
7
  "import": "./dist/src/index.js"
@@ -14,9 +14,9 @@
14
14
  "author": "jhendrix@microsoft.com",
15
15
  "license": "MIT",
16
16
  "dependencies": {
17
- "@alloy-js/core": "~0.19.0 || >= 0.20.0-dev.0",
17
+ "@alloy-js/core": "~0.19.0 || >= 0.20.0-dev.4",
18
18
  "change-case": "^5.4.4",
19
- "marked": "^15.0.12",
19
+ "marked": "^16.1.1",
20
20
  "pathe": "^2.0.3"
21
21
  },
22
22
  "devDependencies": {
@@ -24,7 +24,7 @@
24
24
  "@alloy-js/rollup-plugin": "~0.1.0 || >= 0.1.1-dev.0",
25
25
  "@microsoft/api-extractor": "~7.52.8",
26
26
  "@rollup/plugin-typescript": "^12.1.2",
27
- "concurrently": "^9.1.2",
27
+ "concurrently": "^9.2.0",
28
28
  "typescript": "^5.8.3",
29
29
  "vitest": "^3.2.4"
30
30
  },
@@ -6,7 +6,11 @@ import {
6
6
  } from "../modifiers.js";
7
7
  import { useCSharpNamePolicy } from "../name-policy.js";
8
8
  import { CSharpOutputSymbol } from "../symbols/csharp-output-symbol.js";
9
- import { CSharpMemberScope, useCSharpScope } from "../symbols/scopes.js";
9
+ import {
10
+ CSharpMemberScope,
11
+ useCSharpMemberScope,
12
+ useCSharpScope,
13
+ } from "../symbols/scopes.js";
10
14
  import { Name } from "./Name.jsx";
11
15
 
12
16
  // properties for creating an enum
@@ -78,12 +82,7 @@ export interface EnumMemberProps {
78
82
 
79
83
  // a member within a C# enum
80
84
  export function EnumMember(props: EnumMemberProps) {
81
- const scope = useCSharpScope();
82
- if (scope.kind === "member" && scope.name !== "enum-decl") {
83
- throw new Error(
84
- "can't define an enum member outside of an enum-decl scope",
85
- );
86
- }
85
+ const scope = useCSharpMemberScope(["enum-decl"]);
87
86
 
88
87
  const name = useCSharpNamePolicy().getName(props.name, "enum-member");
89
88
  const thisEnumValueSymbol = new CSharpOutputSymbol(name, {
@@ -70,6 +70,24 @@ describe("modifiers", () => {
70
70
  public abstract partial class TestClass;
71
71
  `);
72
72
  });
73
+
74
+ it("places visibility, attributes, and modifiers in the correct order", () => {
75
+ expect(
76
+ <TestNamespace>
77
+ <ClassDeclaration
78
+ partial
79
+ public
80
+ abstract
81
+ sealed
82
+ name="TestClass"
83
+ attributes={[<Attribute name="Test" />]}
84
+ />
85
+ </TestNamespace>,
86
+ ).toRenderTo(`
87
+ [Test]
88
+ public abstract sealed partial class TestClass;
89
+ `);
90
+ });
73
91
  });
74
92
 
75
93
  describe("base", () => {
@@ -308,7 +326,7 @@ it("declares class with invalid members", () => {
308
326
  );
309
327
 
310
328
  expect(() => toSourceText(decl)).toThrow(
311
- "can't define an enum member outside of an enum-decl scope",
329
+ "Can't define a EnumMember outside of a enum-decl scope",
312
330
  );
313
331
  });
314
332
 
@@ -33,9 +33,9 @@ export interface ClassModifiers {
33
33
 
34
34
  const getClassModifiers = makeModifiers<ClassModifiers>([
35
35
  "abstract",
36
- "partial",
37
36
  "sealed",
38
37
  "static",
38
+ "partial",
39
39
  ]);
40
40
 
41
41
  // properties for creating a class
@@ -14,7 +14,10 @@ import {
14
14
  } from "../../modifiers.js";
15
15
  import { useCSharpNamePolicy } from "../../name-policy.js";
16
16
  import { CSharpOutputSymbol } from "../../symbols/csharp-output-symbol.js";
17
- import { CSharpMemberScope, useCSharpScope } from "../../symbols/scopes.js";
17
+ import {
18
+ CSharpMemberScope,
19
+ useCSharpMemberScope,
20
+ } from "../../symbols/scopes.js";
18
21
  import { ParameterProps, Parameters } from "../parameters/parameters.jsx";
19
22
 
20
23
  /**
@@ -32,15 +35,7 @@ export interface ConstructorProps extends AccessModifiers {
32
35
  }
33
36
 
34
37
  export function Constructor(props: ConstructorProps) {
35
- const scope = useCSharpScope();
36
- if (
37
- scope.kind !== "member" ||
38
- (scope.name !== "class-decl" && scope.name !== "struct-decl")
39
- ) {
40
- throw new Error(
41
- "can't define a class method outside of a class or struct scope",
42
- );
43
- }
38
+ const scope = useCSharpMemberScope(["class-decl", "struct-decl"]);
44
39
 
45
40
  const name = useCSharpNamePolicy().getName(scope.owner!.name, "class-method");
46
41
  const ctorSymbol = new CSharpOutputSymbol(name, {
@@ -7,7 +7,7 @@ import {
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
- import { useCSharpScope } from "../../symbols/scopes.js";
10
+ import { useCSharpMemberScope } from "../../symbols/scopes.js";
11
11
  import { DocWhen } from "../doc/comment.jsx";
12
12
 
13
13
  /** Field modifiers. */
@@ -40,15 +40,7 @@ export function Field(props: FieldProps) {
40
40
  nameElement = "class-member-public";
41
41
  }
42
42
  const name = useCSharpNamePolicy().getName(props.name, nameElement);
43
- const scope = useCSharpScope();
44
- if (
45
- scope.kind !== "member" ||
46
- (scope.name !== "class-decl" && scope.name !== "struct-decl")
47
- ) {
48
- throw new Error(
49
- "can't define a class member outside of a class or struct scope",
50
- );
51
- }
43
+ const scope = useCSharpMemberScope(["class-decl", "struct-decl"]);
52
44
 
53
45
  const memberSymbol = new CSharpOutputSymbol(name, {
54
46
  scope,
@@ -14,7 +14,10 @@ import {
14
14
  } from "../../modifiers.js";
15
15
  import { useCSharpNamePolicy } from "../../name-policy.js";
16
16
  import { CSharpOutputSymbol } from "../../symbols/csharp-output-symbol.js";
17
- import { CSharpMemberScope, useCSharpScope } from "../../symbols/scopes.js";
17
+ import {
18
+ CSharpMemberScope,
19
+ useCSharpMemberScope,
20
+ } from "../../symbols/scopes.js";
18
21
  import { AttributeList, AttributesProp } from "../attributes/attributes.jsx";
19
22
  import { DocWhen } from "../doc/comment.jsx";
20
23
  import { ParameterProps, Parameters } from "../parameters/parameters.jsx";
@@ -77,12 +80,8 @@ export interface InterfaceMethodProps
77
80
  // a C# interface method
78
81
  export function InterfaceMethod(props: InterfaceMethodProps) {
79
82
  const name = useCSharpNamePolicy().getName(props.name, "class-method");
80
- const scope = useCSharpScope();
81
- if (scope.kind !== "member" || scope.name !== "interface-decl") {
82
- throw new Error(
83
- "can't define an interface method outside of an interface scope",
84
- );
85
- }
83
+
84
+ const scope = useCSharpMemberScope(["interface-decl"]);
86
85
 
87
86
  const methodSymbol = new CSharpOutputSymbol(name, {
88
87
  scope,
@@ -15,7 +15,10 @@ import {
15
15
  } from "../../modifiers.js";
16
16
  import { useCSharpNamePolicy } from "../../name-policy.js";
17
17
  import { CSharpOutputSymbol } from "../../symbols/csharp-output-symbol.js";
18
- import { CSharpMemberScope, useCSharpScope } from "../../symbols/scopes.js";
18
+ import {
19
+ CSharpMemberScope,
20
+ useCSharpMemberScope,
21
+ } from "../../symbols/scopes.js";
19
22
  import { AttributeList, AttributesProp } from "../attributes/attributes.jsx";
20
23
  import { DocWhen } from "../doc/comment.jsx";
21
24
 
@@ -85,12 +88,8 @@ export interface InterfacePropertyProps
85
88
  */
86
89
  export function InterfaceProperty(props: InterfacePropertyProps) {
87
90
  const name = useCSharpNamePolicy().getName(props.name, "class-property");
88
- const scope = useCSharpScope();
89
- if (scope.kind !== "member" || scope.name !== "interface-decl") {
90
- throw new Error(
91
- "can't define an interface method outside of an interface scope",
92
- );
93
- }
91
+
92
+ const scope = useCSharpMemberScope(["interface-decl"]);
94
93
 
95
94
  const propertySymbol = new CSharpOutputSymbol(name, {
96
95
  scope,
@@ -15,7 +15,10 @@ import {
15
15
  } from "../../modifiers.js";
16
16
  import { useCSharpNamePolicy } from "../../name-policy.js";
17
17
  import { CSharpOutputSymbol } from "../../symbols/csharp-output-symbol.js";
18
- import { CSharpMemberScope, useCSharpScope } from "../../symbols/scopes.js";
18
+ import {
19
+ CSharpMemberScope,
20
+ useCSharpMemberScope,
21
+ } from "../../symbols/scopes.js";
19
22
  import { AttributeList, AttributesProp } from "../attributes/attributes.jsx";
20
23
  import { DocWhen } from "../doc/comment.jsx";
21
24
  import { ParameterProps, Parameters } from "../parameters/parameters.jsx";
@@ -111,15 +114,7 @@ export interface MethodProps extends AccessModifiers, MethodModifiers {
111
114
  // a C# class method
112
115
  export function Method(props: MethodProps) {
113
116
  const name = useCSharpNamePolicy().getName(props.name, "class-method");
114
- const scope = useCSharpScope();
115
- if (
116
- scope.kind !== "member" ||
117
- (scope.name !== "class-decl" && scope.name !== "struct-decl")
118
- ) {
119
- throw new Error(
120
- "can't define a class method outside of a class or struct scope",
121
- );
122
- }
117
+ const scope = useCSharpMemberScope(["class-decl", "struct-decl"]);
123
118
 
124
119
  const methodSymbol = new CSharpOutputSymbol(name, {
125
120
  scope,
@@ -10,7 +10,7 @@ import {
10
10
  } from "@alloy-js/core";
11
11
  import { useCSharpNamePolicy } from "../../name-policy.js";
12
12
  import { CSharpOutputSymbol } from "../../symbols/csharp-output-symbol.js";
13
- import { useCSharpScope } from "../../symbols/scopes.js";
13
+ import { useCSharpMemberScope } from "../../symbols/scopes.js";
14
14
  import { Name } from "../Name.jsx";
15
15
 
16
16
  export interface ParameterProps {
@@ -27,17 +27,12 @@ export interface ParameterProps {
27
27
  /** Define a parameter to be used in class or interface method. */
28
28
  export function Parameter(props: ParameterProps) {
29
29
  const name = useCSharpNamePolicy().getName(props.name, "parameter");
30
- const scope = useCSharpScope();
31
- if (
32
- scope.kind !== "member" ||
33
- (scope.name !== "constructor-decl" &&
34
- scope.name !== "method-decl" &&
35
- scope.name !== "class-decl")
36
- ) {
37
- throw new Error(
38
- "can't define a parameter outside of a constructor-decl or method-decl scope",
39
- );
40
- }
30
+ const scope = useCSharpMemberScope([
31
+ "constructor-decl",
32
+ "method-decl",
33
+ "class-decl",
34
+ "record-decl",
35
+ ]);
41
36
 
42
37
  const memberSymbol = new CSharpOutputSymbol(name, {
43
38
  scope,
@@ -16,7 +16,10 @@ import {
16
16
  } from "../../modifiers.js";
17
17
  import { useCSharpNamePolicy } from "../../name-policy.js";
18
18
  import { CSharpOutputSymbol } from "../../symbols/csharp-output-symbol.js";
19
- import { CSharpMemberScope, useCSharpScope } from "../../symbols/scopes.js";
19
+ import {
20
+ CSharpMemberScope,
21
+ useCSharpMemberScope,
22
+ } from "../../symbols/scopes.js";
20
23
  import { AttributeList, AttributesProp } from "../attributes/attributes.jsx";
21
24
  import { DocWhen } from "../doc/comment.jsx";
22
25
 
@@ -119,17 +122,11 @@ export interface PropertyProps extends AccessModifiers, PropertyModifiers {
119
122
  */
120
123
  export function Property(props: PropertyProps) {
121
124
  const name = useCSharpNamePolicy().getName(props.name, "class-property");
122
- const scope = useCSharpScope();
123
- if (
124
- scope.kind !== "member" ||
125
- (scope.name !== "class-decl" &&
126
- scope.name !== "record-decl" &&
127
- scope.name !== "struct-decl")
128
- ) {
129
- throw new Error(
130
- "can't define an interface method outside of an interface scope",
131
- );
132
- }
125
+ const scope = useCSharpMemberScope([
126
+ "class-decl",
127
+ "record-decl",
128
+ "struct-decl",
129
+ ]);
133
130
 
134
131
  const propertySymbol = new CSharpOutputSymbol(name, {
135
132
  scope,
@@ -1,9 +1,19 @@
1
+ import { Children, code, refkey } from "@alloy-js/core";
1
2
  import { describe, expect, it } from "vitest";
2
3
  import { TestNamespace } from "../../../test/utils.jsx";
3
4
  import { Property } from "../property/property.jsx";
5
+ import { SourceFile } from "../SourceFile.jsx";
4
6
  import { RecordDeclaration } from "./declaration.jsx";
5
7
 
6
- it("declares class with no members", () => {
8
+ function Wrapper({ children }: { children: Children }) {
9
+ return (
10
+ <TestNamespace>
11
+ <SourceFile path="Test.cs">{children}</SourceFile>
12
+ </TestNamespace>
13
+ );
14
+ }
15
+
16
+ it("declares record with no members", () => {
7
17
  expect(
8
18
  <TestNamespace>
9
19
  <RecordDeclaration name="TestRecord" />
@@ -56,7 +66,7 @@ it("specify doc comment", () => {
56
66
  `);
57
67
  });
58
68
 
59
- it("specify class property inside", () => {
69
+ it("specify record property inside", () => {
60
70
  expect(
61
71
  <TestNamespace>
62
72
  <RecordDeclaration name="TestRecord" doc="This is a test">
@@ -71,3 +81,44 @@ it("specify class property inside", () => {
71
81
  }
72
82
  `);
73
83
  });
84
+
85
+ describe("constructor", () => {
86
+ it("declares primary constructor with args", () => {
87
+ const paramNameRefkey = refkey();
88
+ const paramSizeRefkey = refkey();
89
+
90
+ const ctorParams = [
91
+ {
92
+ name: "name",
93
+ type: "string",
94
+ refkey: paramNameRefkey,
95
+ },
96
+ {
97
+ name: "size",
98
+ type: "int",
99
+ refkey: paramSizeRefkey,
100
+ },
101
+ ];
102
+
103
+ expect(
104
+ <Wrapper>
105
+ <RecordDeclaration public name="Test" primaryConstructor={ctorParams}>
106
+ <Property
107
+ name="PrettyName"
108
+ type="string"
109
+ get
110
+ initializer={code`$"{${paramNameRefkey}} {${paramSizeRefkey}}"`}
111
+ />
112
+ </RecordDeclaration>
113
+ </Wrapper>,
114
+ ).toRenderTo(`
115
+ namespace TestCode
116
+ {
117
+ public record Test(string name, int size)
118
+ {
119
+ string PrettyName { get; } = $"{name} {size}";
120
+ }
121
+ }
122
+ `);
123
+ });
124
+ });
@@ -10,6 +10,7 @@ import { CSharpOutputSymbol } from "../../symbols/csharp-output-symbol.js";
10
10
  import { CSharpMemberScope } from "../../symbols/scopes.js";
11
11
  import { DocWhen } from "../doc/comment.jsx";
12
12
  import { Name } from "../Name.jsx";
13
+ import { ParameterProps, Parameters } from "../parameters/parameters.jsx";
13
14
 
14
15
  export interface RecordModifiers {
15
16
  readonly partial?: boolean;
@@ -28,6 +29,24 @@ export interface RecordDeclarationProps
28
29
  doc?: core.Children;
29
30
  refkey?: core.Refkey;
30
31
  typeParameters?: Record<string, core.Refkey>;
32
+
33
+ /**
34
+ * Set the primary constructor parameters
35
+ * @example
36
+ * ```tsx
37
+ * <ClassDeclaration name="MyClass" primaryConstructor={[
38
+ * {name: "value", type: "int"}
39
+ * ]}>
40
+ * ```
41
+ * This will produce:
42
+ * ```csharp
43
+ * public class MyClass(int value)
44
+ * {
45
+ *
46
+ * }
47
+ * ```
48
+ */
49
+ primaryConstructor?: ParameterProps[];
31
50
  }
32
51
 
33
52
  /**
@@ -99,6 +118,11 @@ export function RecordDeclaration(props: RecordDeclarationProps) {
99
118
  <DocWhen doc={props.doc} />
100
119
  {modifiers}record <Name />
101
120
  {typeParams}
121
+ {props.primaryConstructor && (
122
+ <core.Scope value={thisRecordScope}>
123
+ <Parameters parameters={props.primaryConstructor} />
124
+ </core.Scope>
125
+ )}
102
126
  {props.children ?
103
127
  <core.Block newline>
104
128
  <core.Scope value={thisRecordScope}>{props.children}</core.Scope>
@@ -39,3 +39,23 @@ export type CSharpOutputScope = CSharpMemberScope | CSharpNamespaceScope;
39
39
  export function useCSharpScope(): CSharpOutputScope {
40
40
  return core.useScope() as CSharpOutputScope;
41
41
  }
42
+
43
+ export function useCSharpMemberScope<T extends unknown[]>(
44
+ names: T,
45
+ ): CSharpMemberScope & { name: T[number] } {
46
+ const scope = useCSharpScope();
47
+ assertMemberOfScope(scope, names);
48
+ return scope;
49
+ }
50
+
51
+ export function assertMemberOfScope<T extends unknown[]>(
52
+ scope: CSharpOutputScope,
53
+ names: T,
54
+ ): asserts scope is CSharpMemberScope & { name: T[number] } {
55
+ if (scope.kind !== "member" || !names.includes(scope.name as T[number])) {
56
+ const context = core.getContext();
57
+ throw new Error(
58
+ `Can't define a ${context?.componentOwner?.component.name} outside of a ${names.join(" or ")} scope`,
59
+ );
60
+ }
61
+ }