@alloy-js/csharp 0.21.0-dev.15 → 0.21.0-dev.17

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alloy-js/csharp",
3
- "version": "0.21.0-dev.15",
3
+ "version": "0.21.0-dev.17",
4
4
  "description": "Alloy components for CSharp language.",
5
5
  "exports": {
6
6
  ".": {
@@ -1,5 +1,5 @@
1
1
  import { List, namekey, refkey } from "@alloy-js/core";
2
- import { expect, it } from "vitest";
2
+ import { describe, expect, it } from "vitest";
3
3
  import { TestNamespace } from "../../../test/utils.jsx";
4
4
  import { VarDeclaration } from "./declaration.jsx";
5
5
 
@@ -66,3 +66,17 @@ it("links namekey", () => {
66
66
  var testVar2 = testVar;
67
67
  `);
68
68
  });
69
+
70
+ describe("modifiers", () => {
71
+ it.each(["const", "using"])("%s", (mod) => {
72
+ expect(
73
+ <TestNamespace>
74
+ <VarDeclaration {...{ [mod]: true }} name="test" type="object">
75
+ a
76
+ </VarDeclaration>
77
+ </TestNamespace>,
78
+ ).toRenderTo(`
79
+ ${mod} object test = a;
80
+ `);
81
+ });
82
+ });
@@ -7,11 +7,13 @@ import {
7
7
  Namekey,
8
8
  Refkey,
9
9
  } from "@alloy-js/core";
10
+ import { computeModifiersPrefix, makeModifiers } from "../../modifiers.js";
10
11
  import { createVariableSymbol } from "../../symbols/factories.js";
11
12
 
12
13
  /** Props for {@link VarDeclaration} component */
13
14
  export interface VarDeclarationProps
14
- extends Omit<DeclarationProps, "nameKind"> {
15
+ extends Omit<DeclarationProps, "nameKind">,
16
+ VarModifiers {
15
17
  /** Variable name */
16
18
  name: string | Namekey;
17
19
  /** Type of the variable declaration. If not specified, defaults to "var" */
@@ -20,11 +22,18 @@ export interface VarDeclarationProps
20
22
  refkey?: Refkey;
21
23
  /** Variable value */
22
24
  children?: Children;
25
+ }
23
26
 
27
+ export interface VarModifiers {
24
28
  /** Constant variable. Add the const modifier. */
25
- const?: boolean;
29
+ readonly const?: boolean;
30
+
31
+ /** Disposable variable. Add the using modifier. */
32
+ readonly using?: boolean;
26
33
  }
27
34
 
35
+ const getModifiers = makeModifiers<VarModifiers>(["const", "using"]);
36
+
28
37
  /**
29
38
  * Render a variable declaration
30
39
  *
@@ -60,7 +69,7 @@ export function VarDeclaration(props: VarDeclarationProps) {
60
69
  }
61
70
  return (
62
71
  <Declaration symbol={sym}>
63
- {props.const ? "const " : ""}
72
+ {computeModifiersPrefix([getModifiers(props)])}
64
73
  <TypeSlot>{props.type ?? "var"}</TypeSlot> <Name /> ={" "}
65
74
  <ValueSlot>{props.children}</ValueSlot>;
66
75
  </Declaration>
@@ -191,3 +191,23 @@ it("has built-ins available", () => {
191
191
  var directoryName = info.DirectoryName?.Length;
192
192
  `);
193
193
  });
194
+
195
+ it("can render multiple times", () => {
196
+ const System = createLibrary("System", {
197
+ String: {
198
+ kind: "class",
199
+ members: {},
200
+ },
201
+ });
202
+ expect(<TestNamespace>{System.String}</TestNamespace>).toRenderTo(`
203
+ using System;
204
+
205
+ String
206
+ `);
207
+
208
+ expect(<TestNamespace>{System.String}</TestNamespace>).toRenderTo(`
209
+ using System;
210
+
211
+ String
212
+ `);
213
+ });
@@ -96,16 +96,15 @@ export type LibraryFrom<T> = {
96
96
  } & LibrarySymbolReference;
97
97
 
98
98
  interface InternalContext {
99
- binder(): Binder;
100
- ownerSymbol(): NamedTypeSymbol;
99
+ ownerSymbol(binder: Binder | undefined): NamedTypeSymbol;
101
100
  }
102
101
 
103
102
  export function createLibrary<T extends Record<string, Descriptor>>(
104
103
  rootNs: string,
105
104
  props: T,
106
105
  ): LibraryFrom<T> {
107
- let binder: Binder | undefined = undefined;
108
- let ownerSymbol: NamespaceSymbol | undefined = undefined;
106
+ const ownerSymbolPerBinder = new WeakMap<Binder, NamespaceSymbol>();
107
+
109
108
  const namespaceNames = rootNs.split(".");
110
109
 
111
110
  return createSymbolEntry(
@@ -115,33 +114,24 @@ export function createLibrary<T extends Record<string, Descriptor>>(
115
114
  members: props,
116
115
  },
117
116
  {
118
- binder() {
119
- if (!binder) {
120
- binder = useBinder();
121
- }
122
- return binder!;
123
- },
124
- ownerSymbol() {
125
- if (ownerSymbol) {
126
- return ownerSymbol;
127
- }
128
-
129
- const binder = this.binder();
130
- ownerSymbol = getGlobalNamespace(binder);
131
- for (const name of namespaceNames.slice(0, -1)) {
132
- if (ownerSymbol!.members.symbolNames.has(name)) {
133
- ownerSymbol = ownerSymbol!.members.symbolNames.get(
134
- name,
135
- )! as NamespaceSymbol;
136
- } else {
137
- ownerSymbol = new NamespaceSymbol(name, ownerSymbol!, {
138
- binder,
139
- refkeys: refkey(),
140
- });
117
+ ownerSymbol(binder: Binder | undefined) {
118
+ return mapGet(ownerSymbolPerBinder, binder, () => {
119
+ let ownerSymbol = getGlobalNamespace(binder);
120
+ for (const name of namespaceNames.slice(0, -1)) {
121
+ if (ownerSymbol!.members.symbolNames.has(name)) {
122
+ ownerSymbol = ownerSymbol!.members.symbolNames.get(
123
+ name,
124
+ )! as NamespaceSymbol;
125
+ } else {
126
+ ownerSymbol = new NamespaceSymbol(name, ownerSymbol!, {
127
+ binder,
128
+ refkeys: refkey(),
129
+ });
130
+ }
141
131
  }
142
- }
143
132
 
144
- return ownerSymbol;
133
+ return ownerSymbol;
134
+ });
145
135
  },
146
136
  },
147
137
  ) as LibraryFrom<T>;
@@ -152,24 +142,26 @@ function createSymbolEntry(
152
142
  descriptor: Descriptor,
153
143
  context: InternalContext,
154
144
  ): LibrarySymbolReference {
155
- let symbol: CSharpSymbol | undefined = undefined;
156
-
157
- function getSymbol() {
158
- if (symbol) return symbol;
145
+ const symbols = new WeakMap<Binder, CSharpSymbol>();
159
146
 
160
- symbol = createSymbolFromDescriptor(
161
- name,
162
- descriptor,
163
- context,
164
- initializeMembers,
147
+ function getSymbol(binder: Binder | undefined) {
148
+ // We cache symbols per binder to ensure we only create one symbol. We also
149
+ // track an unbound symbol for cases where there is no binder (mostly
150
+ // tests).
151
+ return mapGet(symbols, binder, () =>
152
+ createSymbolFromDescriptor(
153
+ name,
154
+ binder,
155
+ descriptor,
156
+ context,
157
+ initializeMembers,
158
+ ),
165
159
  );
166
- return symbol;
167
160
  }
168
161
 
169
162
  const newContext: InternalContext = {
170
- binder: context.binder,
171
- ownerSymbol() {
172
- return getSymbol() as NamedTypeSymbol;
163
+ ownerSymbol(binder) {
164
+ return getSymbol(binder) as NamedTypeSymbol;
173
165
  },
174
166
  };
175
167
 
@@ -182,10 +174,10 @@ function createSymbolEntry(
182
174
 
183
175
  const obj: LibrarySymbolReference & Record<string, unknown> = {
184
176
  [REFKEYABLE]() {
185
- return getSymbol().refkeys[0];
177
+ return getSymbol(useBinder()).refkeys[0];
186
178
  },
187
179
  [TO_SYMBOL]() {
188
- return getSymbol();
180
+ return getSymbol(useBinder());
189
181
  },
190
182
  };
191
183
 
@@ -209,12 +201,12 @@ function createSymbolEntry(
209
201
 
210
202
  function createSymbolFromDescriptor(
211
203
  name: string,
204
+ binder: Binder | undefined,
212
205
  descriptor: Descriptor,
213
206
  context: InternalContext,
214
207
  lazyMemberInitializer: () => void,
215
208
  ): CSharpSymbol {
216
- const ownerSymbol = context.ownerSymbol();
217
- const binder = context.binder();
209
+ const ownerSymbol = context.ownerSymbol(binder);
218
210
 
219
211
  switch (descriptor.kind) {
220
212
  case "namespace":
@@ -268,3 +260,37 @@ function createSymbolFromDescriptor(
268
260
  throw "Unsupported";
269
261
  }
270
262
  }
263
+
264
+ const defaultsPerMap = new WeakMap<object, unknown>();
265
+
266
+ function mapGet<T extends WeakKey, V>(
267
+ map: WeakMap<T, V>,
268
+ key: T | undefined,
269
+ ): V | undefined;
270
+ function mapGet<T extends WeakKey, V>(
271
+ map: WeakMap<T, V>,
272
+ key: T | undefined,
273
+ init: () => V,
274
+ ): V;
275
+ function mapGet<T extends WeakKey, V>(
276
+ map: WeakMap<T, V>,
277
+ key: T | undefined,
278
+ init?: () => V,
279
+ ): V | undefined {
280
+ if (key === undefined) {
281
+ // Use a per-map default store when callers request a value for an undefined key.
282
+ let value = defaultsPerMap.get(map as unknown as object) as V | undefined;
283
+ if (value === undefined && init) {
284
+ value = init();
285
+ defaultsPerMap.set(map as unknown as object, value);
286
+ }
287
+ return value;
288
+ }
289
+
290
+ let value = map.get(key);
291
+ if (value === undefined && init) {
292
+ value = init();
293
+ map.set(key, value);
294
+ }
295
+ return value;
296
+ }
package/temp/api.json CHANGED
@@ -16856,6 +16856,15 @@
16856
16856
  "kind": "Content",
16857
16857
  "text": ", \"nameKind\">"
16858
16858
  },
16859
+ {
16860
+ "kind": "Content",
16861
+ "text": ", "
16862
+ },
16863
+ {
16864
+ "kind": "Reference",
16865
+ "text": "VarModifiers",
16866
+ "canonicalReference": "@alloy-js/csharp!VarModifiers:interface"
16867
+ },
16859
16868
  {
16860
16869
  "kind": "Content",
16861
16870
  "text": " "
@@ -16894,33 +16903,6 @@
16894
16903
  "endIndex": 2
16895
16904
  }
16896
16905
  },
16897
- {
16898
- "kind": "PropertySignature",
16899
- "canonicalReference": "@alloy-js/csharp!VarDeclarationProps#const:member",
16900
- "docComment": "/**\n * Constant variable. Add the const modifier.\n */\n",
16901
- "excerptTokens": [
16902
- {
16903
- "kind": "Content",
16904
- "text": "const?: "
16905
- },
16906
- {
16907
- "kind": "Content",
16908
- "text": "boolean"
16909
- },
16910
- {
16911
- "kind": "Content",
16912
- "text": ";"
16913
- }
16914
- ],
16915
- "isReadonly": false,
16916
- "isOptional": true,
16917
- "releaseTag": "Public",
16918
- "name": "const",
16919
- "propertyTypeTokenRange": {
16920
- "startIndex": 1,
16921
- "endIndex": 2
16922
- }
16923
- },
16924
16906
  {
16925
16907
  "kind": "PropertySignature",
16926
16908
  "canonicalReference": "@alloy-js/csharp!VarDeclarationProps#name:member",
@@ -17014,8 +16996,84 @@
17014
16996
  {
17015
16997
  "startIndex": 1,
17016
16998
  "endIndex": 5
16999
+ },
17000
+ {
17001
+ "startIndex": 6,
17002
+ "endIndex": 7
17017
17003
  }
17018
17004
  ]
17005
+ },
17006
+ {
17007
+ "kind": "Interface",
17008
+ "canonicalReference": "@alloy-js/csharp!VarModifiers:interface",
17009
+ "docComment": "",
17010
+ "excerptTokens": [
17011
+ {
17012
+ "kind": "Content",
17013
+ "text": "export interface VarModifiers "
17014
+ }
17015
+ ],
17016
+ "fileUrlPath": "src/components/var/declaration.tsx",
17017
+ "releaseTag": "Public",
17018
+ "name": "VarModifiers",
17019
+ "preserveMemberOrder": false,
17020
+ "members": [
17021
+ {
17022
+ "kind": "PropertySignature",
17023
+ "canonicalReference": "@alloy-js/csharp!VarModifiers#const:member",
17024
+ "docComment": "/**\n * Constant variable. Add the const modifier.\n */\n",
17025
+ "excerptTokens": [
17026
+ {
17027
+ "kind": "Content",
17028
+ "text": "readonly const?: "
17029
+ },
17030
+ {
17031
+ "kind": "Content",
17032
+ "text": "boolean"
17033
+ },
17034
+ {
17035
+ "kind": "Content",
17036
+ "text": ";"
17037
+ }
17038
+ ],
17039
+ "isReadonly": true,
17040
+ "isOptional": true,
17041
+ "releaseTag": "Public",
17042
+ "name": "const",
17043
+ "propertyTypeTokenRange": {
17044
+ "startIndex": 1,
17045
+ "endIndex": 2
17046
+ }
17047
+ },
17048
+ {
17049
+ "kind": "PropertySignature",
17050
+ "canonicalReference": "@alloy-js/csharp!VarModifiers#using:member",
17051
+ "docComment": "/**\n * Disposable variable. Add the using modifier.\n */\n",
17052
+ "excerptTokens": [
17053
+ {
17054
+ "kind": "Content",
17055
+ "text": "readonly using?: "
17056
+ },
17057
+ {
17058
+ "kind": "Content",
17059
+ "text": "boolean"
17060
+ },
17061
+ {
17062
+ "kind": "Content",
17063
+ "text": ";"
17064
+ }
17065
+ ],
17066
+ "isReadonly": true,
17067
+ "isOptional": true,
17068
+ "releaseTag": "Public",
17069
+ "name": "using",
17070
+ "propertyTypeTokenRange": {
17071
+ "startIndex": 1,
17072
+ "endIndex": 2
17073
+ }
17074
+ }
17075
+ ],
17076
+ "extendsTokenRanges": []
17019
17077
  }
17020
17078
  ]
17021
17079
  }