@alloy-js/csharp 0.21.0-dev.16 → 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.16",
3
+ "version": "0.21.0-dev.17",
4
4
  "description": "Alloy components for CSharp language.",
5
5
  "exports": {
6
6
  ".": {
@@ -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
+ }